2017-05-28 12:00:01 +00:00
|
|
|
package Presenter.Algorithms;
|
|
|
|
|
2017-06-29 21:22:34 +00:00
|
|
|
import Model.Interval;
|
2017-06-26 14:01:54 +00:00
|
|
|
import Model.Line;
|
|
|
|
import Model.Point;
|
2017-06-29 15:32:54 +00:00
|
|
|
import Presenter.Presenter;
|
|
|
|
|
2017-06-29 21:22:34 +00:00
|
|
|
import java.util.*;
|
2017-06-26 14:01:54 +00:00
|
|
|
|
2017-05-28 12:00:01 +00:00
|
|
|
/**
|
|
|
|
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
|
|
|
|
*
|
|
|
|
* @Author: Armin Wolf
|
|
|
|
* @Email: a_wolf28@uni-muenster.de
|
|
|
|
* @Date: 28.05.2017.
|
|
|
|
*/
|
2017-06-26 14:01:54 +00:00
|
|
|
public class TheilSenEstimator extends Observable implements Algorithm {
|
|
|
|
|
2017-06-29 15:32:54 +00:00
|
|
|
private final Double POSITIV_INF = 99999.0;
|
|
|
|
private final Double NEGATIV_INF = -99999.0;
|
|
|
|
|
|
|
|
|
|
|
|
private Presenter presenter;
|
|
|
|
private ArrayList<Line> setOfLines;
|
|
|
|
private ArrayList<Point> setOfIntersections;
|
|
|
|
private ArrayList<Point> intervalIntersections;
|
|
|
|
private ArrayList<Double> sampledIntersections;
|
|
|
|
|
|
|
|
|
|
|
|
//wird benötigt um den y Achsenabschnitt zu Berechnen
|
|
|
|
private ArrayList<Double> yCoordinates;
|
|
|
|
private ArrayList<Double> xCoordinates;
|
2017-06-26 14:01:54 +00:00
|
|
|
|
|
|
|
|
2017-06-29 15:32:54 +00:00
|
|
|
//Hilfsvariablen (siehe original Paper)
|
|
|
|
private Double j;
|
|
|
|
private Integer jA;
|
|
|
|
private Integer jB;
|
|
|
|
private Double r;
|
|
|
|
private Integer n;
|
|
|
|
private Double N;
|
|
|
|
private Integer k;
|
2017-06-29 11:10:15 +00:00
|
|
|
|
2017-06-29 15:32:54 +00:00
|
|
|
//Intervall und die temporaeren Grenzen
|
|
|
|
private Interval interval;
|
|
|
|
private Double aVariant;
|
|
|
|
private Double bVariant;
|
2017-06-26 14:01:54 +00:00
|
|
|
|
2017-08-01 19:59:33 +00:00
|
|
|
private Double slope;
|
|
|
|
private Double yInterception;
|
|
|
|
|
2017-06-26 14:01:54 +00:00
|
|
|
|
2017-06-29 15:32:54 +00:00
|
|
|
public TheilSenEstimator(LinkedList<Line> setOfLines, LinkedList<Point> setOfIntersections, Presenter presenter) {
|
|
|
|
this.presenter = presenter;
|
|
|
|
this.setOfLines = new ArrayList<>(setOfLines);
|
|
|
|
this.setOfIntersections = new ArrayList<>(setOfIntersections);
|
|
|
|
this.intervalIntersections = new ArrayList<>(setOfIntersections);
|
|
|
|
|
|
|
|
this.n = setOfLines.size();
|
|
|
|
this.sampledIntersections = new ArrayList<>();
|
|
|
|
this.yCoordinates = new ArrayList<>();
|
|
|
|
this.xCoordinates = new ArrayList<>();
|
|
|
|
this.N = BinomialCoeffizient.run(n, 2);
|
2017-08-02 20:42:01 +00:00
|
|
|
this.k = Integer.valueOf((int) (N * 0.5))-1;
|
2017-06-29 15:32:54 +00:00
|
|
|
|
|
|
|
//Koordinaten werden gespeichert damit am ende
|
|
|
|
//der y Achsenabschnitt berechnet werden kann
|
|
|
|
for (Point l : setOfIntersections) {
|
|
|
|
yCoordinates.add(l.getY());
|
|
|
|
xCoordinates.add(l.getX());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-01 19:59:33 +00:00
|
|
|
public TheilSenEstimator(LinkedList<Line> setOfLines, LinkedList<Point> setOfIntersections) {
|
|
|
|
this(setOfLines,setOfIntersections,null);
|
|
|
|
}
|
|
|
|
|
2017-06-29 15:32:54 +00:00
|
|
|
/**
|
|
|
|
* Randomisierter Algorithmus zur Berechnung des Theil-Sen Schätzers.
|
|
|
|
* Algorithmus stammt aus dem Paper:
|
|
|
|
* "Jiri Matousek, Randomized optimal algorithm for slope selection,
|
|
|
|
* Information Processing Letters 39 (1991) 183-187
|
|
|
|
*/
|
|
|
|
public void run() {
|
|
|
|
//damit eine initiale Ordnung herscht
|
2017-06-30 08:34:01 +00:00
|
|
|
//Collections.sort(intervalIntersections);
|
2017-06-29 15:32:54 +00:00
|
|
|
|
|
|
|
interval = new Interval(NEGATIV_INF, POSITIV_INF);
|
2017-08-02 20:42:01 +00:00
|
|
|
r = (double) n;
|
2017-06-29 15:32:54 +00:00
|
|
|
while (true) {
|
|
|
|
if (this.N <= n) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
//Anzahl der Schnittpunkte im Intervall [-Inf, a)
|
|
|
|
int numberOfIntersections = getIntervalSize(NEGATIV_INF, interval.getLower(), setOfIntersections);
|
|
|
|
|
|
|
|
//Randomized Interpolating Search
|
|
|
|
j = (r / N) * (double) (k - numberOfIntersections);
|
2017-08-02 20:42:01 +00:00
|
|
|
jA = (int) Math.max(1, Math.floor(j - (3.0 * Math.sqrt(r))));
|
|
|
|
jB = (int) Math.min(r, Math.floor(j + (3.0 * Math.sqrt(r))));
|
2017-06-29 15:32:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* Suche nach einem passenderen und kleineren Intervall
|
|
|
|
Schleife terminiert wenn die das k-te Elemnet zwischen aVariant und bVariant liegt und
|
|
|
|
das Intrvall weniger als 11*N / sqrt(r) Elemente besitzt */
|
|
|
|
do {
|
|
|
|
//zufällige Stichprobe
|
2017-08-02 20:42:01 +00:00
|
|
|
sampledIntersections = RandomSampler.run(intervalIntersections, r);
|
2017-06-29 21:22:34 +00:00
|
|
|
aVariant = FastElementSelector.randomizedSelect(sampledIntersections, jA);
|
|
|
|
bVariant = FastElementSelector.randomizedSelect(sampledIntersections, jB);
|
2017-06-29 15:32:54 +00:00
|
|
|
} while (!checkCondition());
|
|
|
|
|
|
|
|
interval.setLower(aVariant);
|
|
|
|
interval.setUpper(bVariant);
|
|
|
|
N = Double.valueOf(getIntervalSize(interval.getLower(), interval.getUpper()));
|
|
|
|
intervalIntersections = getIntervalElements(interval.getLower(), interval.getUpper());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Diese Funktion überprüft ob die Bedingung für das Interval erfüllt ist. Dabei muss der k-te
|
|
|
|
* Schnittpunkt in diesem Interval enthalten sein. des weiteren soll die Anzahl der Schnittpunkte
|
|
|
|
* im Interval kleiner oder gleich dem Term: (11*N)/sqrt(r) sein.
|
|
|
|
*
|
|
|
|
* @return Boolscher Wert ob die Bedingung erfüllt ist
|
|
|
|
*/
|
|
|
|
private Boolean checkCondition() {
|
2017-06-29 21:22:34 +00:00
|
|
|
Double kthElement = FastElementSelector.randomizedSelect(xCoordinates, k);
|
2017-08-02 20:42:01 +00:00
|
|
|
Boolean cond1 = (kthElement > aVariant) && (kthElement <= bVariant);
|
|
|
|
Boolean cond2 = (getIntervalSize(aVariant+1, bVariant+1, intervalIntersections) <= ((11 * N) / Math.sqrt(r)));
|
2017-06-29 21:22:34 +00:00
|
|
|
|
2017-06-29 15:32:54 +00:00
|
|
|
return cond1 && cond2;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Berechne wieviele von den Schnittpunkten in dem Interval zwischen <code>a</code> und <code>b</code>
|
|
|
|
* enthalten sind.
|
|
|
|
*
|
|
|
|
* @param a untere Grenze des Intervals
|
|
|
|
* @param b obere Grenze des Intrvals
|
|
|
|
* @return Anzahl der Schnittpunkte im Interval [a,b)
|
|
|
|
*/
|
|
|
|
public int getIntervalSize(double a, double b, ArrayList<Point> set) {
|
|
|
|
int counter = 0;
|
|
|
|
for (Point x : set) {
|
|
|
|
if (x.getX() >= a && x.getX() < b) {
|
|
|
|
counter++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return counter;
|
2017-06-26 14:01:54 +00:00
|
|
|
}
|
|
|
|
|
2017-06-29 15:32:54 +00:00
|
|
|
/**
|
|
|
|
* Berechne wieviele von den Schnittpunkten in dem Interval zwischen <code>a</code> und <code>b</code>
|
|
|
|
* enthalten sind.
|
|
|
|
*
|
|
|
|
* @param a untere Grenze des Intervals
|
|
|
|
* @param b obere Grenze des Intrvals
|
|
|
|
* @return Anzahl der Schnittpunkte im Interval [a,b)
|
|
|
|
*/
|
|
|
|
public int getIntervalSize(double a, double b) {
|
|
|
|
int counter = 0;
|
|
|
|
for (int i=0;i<intervalIntersections.size();i++) {
|
|
|
|
Point x = intervalIntersections.get(i);
|
|
|
|
if (x.getX() >= a && x.getX() < b) {
|
|
|
|
counter++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return counter;
|
2017-06-26 14:01:54 +00:00
|
|
|
}
|
2017-06-29 15:32:54 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Berechne wieviele von den Schnittpunkten in dem Interval zwischen <code>a</code> und <code>b</code>
|
|
|
|
* enthalten sind. Zusätzlich werden diese Schnittpunkte in einer Liste festgehalten und diese werden
|
|
|
|
* zurückgeliefert.
|
|
|
|
*
|
|
|
|
* @param a untere Grenze des Intervals
|
|
|
|
* @param b obere Grenze des Intrvals
|
|
|
|
* @return Liste der Schnittpunkte die im Interval [a,b) vertreten sind
|
|
|
|
*/
|
|
|
|
public ArrayList<Point> getIntervalElements(double a, double b) {
|
|
|
|
ArrayList<Point> list = new ArrayList<>();
|
|
|
|
for (int i=0;i<intervalIntersections.size();i++) {
|
|
|
|
Point x = intervalIntersections.get(i);
|
|
|
|
if (x.getX() >= a && x.getX() < b) {
|
|
|
|
list.add(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return list;
|
2017-06-27 08:08:32 +00:00
|
|
|
}
|
|
|
|
|
2017-06-29 15:32:54 +00:00
|
|
|
@Override
|
|
|
|
public void getResult() {
|
2017-08-02 05:40:08 +00:00
|
|
|
double m, x;
|
|
|
|
double b, y;
|
|
|
|
|
|
|
|
Collections.sort(xCoordinates);
|
|
|
|
Collections.sort(yCoordinates);
|
|
|
|
int n = xCoordinates.size();
|
|
|
|
if (n % 2 == 0) {
|
|
|
|
x = 0.5 * (xCoordinates.get((n / 2) - 1) + xCoordinates.get((n / 2)));
|
|
|
|
y = 0.5 * (yCoordinates.get((n / 2) - 1) + yCoordinates.get((n / 2)));
|
|
|
|
} else {
|
|
|
|
x = xCoordinates.get(((n + 1) / 2) - 1);
|
|
|
|
y = yCoordinates.get(((n + 1) / 2) - 1);
|
|
|
|
}
|
2017-06-29 15:32:54 +00:00
|
|
|
|
2017-08-02 05:40:08 +00:00
|
|
|
ArrayList<Point> resultSt = getIntervalElements(interval.getLower(), interval.getUpper());
|
|
|
|
int size = resultSt.size();
|
|
|
|
if (size % 2 == 0) {
|
|
|
|
m = 0.5 * (resultSt.get((size / 2) - 1).getX() + resultSt.get((size / 2)).getX());
|
|
|
|
} else {
|
|
|
|
m = resultSt.get(((size + 1) / 2) - 1).getX();
|
|
|
|
}
|
2017-06-29 15:32:54 +00:00
|
|
|
|
2017-08-02 05:40:08 +00:00
|
|
|
b = (x * m) - y;
|
2017-06-29 15:32:54 +00:00
|
|
|
|
2017-08-02 05:40:08 +00:00
|
|
|
slope = m;
|
|
|
|
yInterception = b;
|
2017-06-29 15:32:54 +00:00
|
|
|
|
2017-08-02 05:40:08 +00:00
|
|
|
if (presenter != null) {
|
|
|
|
setChanged();
|
2017-08-01 19:59:33 +00:00
|
|
|
|
2017-06-29 15:32:54 +00:00
|
|
|
String[] result = new String[]{"ts", m + "", b + ""};
|
|
|
|
notifyObservers(result);
|
|
|
|
}
|
2017-06-29 11:10:15 +00:00
|
|
|
}
|
2017-06-29 15:32:54 +00:00
|
|
|
|
|
|
|
|
2017-08-01 19:59:33 +00:00
|
|
|
public Double getSlope() {
|
|
|
|
return slope;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Double getyInterception() {
|
|
|
|
return yInterception;
|
|
|
|
}
|
2017-05-28 12:00:01 +00:00
|
|
|
}
|