2020-03-21 00:37:09 +00:00
|
|
|
package de.wwwu.awolf.presenter.algorithms.advanced;
|
|
|
|
|
|
|
|
import de.wwwu.awolf.model.Interval;
|
|
|
|
import de.wwwu.awolf.model.Line;
|
|
|
|
import de.wwwu.awolf.model.Point;
|
|
|
|
import de.wwwu.awolf.model.communication.AlgorithmData;
|
|
|
|
import de.wwwu.awolf.model.communication.Data;
|
|
|
|
import de.wwwu.awolf.model.communication.SubscriberType;
|
|
|
|
import de.wwwu.awolf.presenter.Presenter;
|
|
|
|
import de.wwwu.awolf.presenter.algorithms.Algorithm;
|
2020-03-23 06:58:40 +00:00
|
|
|
import de.wwwu.awolf.presenter.util.*;
|
|
|
|
|
|
|
|
import java.util.*;
|
2020-03-21 00:37:09 +00:00
|
|
|
import java.util.concurrent.Flow;
|
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.
|
|
|
|
*/
|
2020-03-21 00:37:09 +00:00
|
|
|
public class TheilSenEstimator implements Algorithm, Flow.Publisher<Data> {
|
2017-06-26 14:01:54 +00:00
|
|
|
|
2020-03-24 07:04:07 +00:00
|
|
|
private final double POSITIV_INF = 9999.0;
|
|
|
|
private final double NEGATIV_INF = -9999.0;
|
2019-08-01 05:19:04 +00:00
|
|
|
private final double EPSILON = 0.00001;
|
2020-03-21 00:37:09 +00:00
|
|
|
private List<Line> setOfLines;
|
2017-06-29 15:32:54 +00:00
|
|
|
//Hilfsvariablen (siehe original Paper)
|
2019-08-01 05:19:04 +00:00
|
|
|
private double j;
|
|
|
|
private int jA;
|
|
|
|
private int jB;
|
|
|
|
private double r;
|
|
|
|
private int n;
|
|
|
|
private double N;
|
|
|
|
private int k;
|
2017-06-29 15:32:54 +00:00
|
|
|
//Intervall und die temporaeren Grenzen
|
|
|
|
private Interval interval;
|
2019-08-01 05:19:04 +00:00
|
|
|
private double aVariant;
|
|
|
|
private double bVariant;
|
|
|
|
private double slope;
|
|
|
|
private double yInterception;
|
2020-03-21 00:37:09 +00:00
|
|
|
private Flow.Subscriber<? super AlgorithmData> subscriber;
|
2017-08-01 19:59:33 +00:00
|
|
|
|
2017-10-15 13:21:53 +00:00
|
|
|
/**
|
|
|
|
* Konstruktor
|
2020-03-20 17:08:18 +00:00
|
|
|
*
|
|
|
|
* @param setOfLines Liste der Geraden
|
|
|
|
* @param presenter Presenter (Beobachter)
|
2017-10-15 13:21:53 +00:00
|
|
|
*/
|
2020-03-23 06:58:40 +00:00
|
|
|
public TheilSenEstimator(List<Line> setOfLines, Presenter presenter) {
|
2020-03-21 00:37:09 +00:00
|
|
|
|
2017-06-29 15:32:54 +00:00
|
|
|
this.setOfLines = new ArrayList<>(setOfLines);
|
|
|
|
|
|
|
|
this.n = setOfLines.size();
|
|
|
|
this.N = BinomialCoeffizient.run(n, 2);
|
2017-09-23 12:13:09 +00:00
|
|
|
//this.k = Integer.valueOf((int) (N * 0.5)) - 1;
|
|
|
|
this.k = (int) (N / 2);
|
2017-10-23 15:48:36 +00:00
|
|
|
|
|
|
|
interval = new Interval(NEGATIV_INF, POSITIV_INF);
|
2020-03-21 00:37:09 +00:00
|
|
|
subscribe(presenter);
|
2017-06-29 15:32:54 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 13:21:53 +00:00
|
|
|
/**
|
|
|
|
* Konstruktor
|
2020-03-20 17:08:18 +00:00
|
|
|
*
|
|
|
|
* @param setOfLines Liste der Geraden
|
2017-10-15 13:21:53 +00:00
|
|
|
*/
|
2020-03-23 06:58:40 +00:00
|
|
|
public TheilSenEstimator(List<Line> setOfLines) {
|
|
|
|
this(setOfLines, null);
|
2017-08-01 19:59:33 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
*/
|
2020-03-21 19:54:03 +00:00
|
|
|
public Line call() {
|
2017-06-29 15:32:54 +00:00
|
|
|
//damit eine initiale Ordnung herscht
|
2017-06-30 08:34:01 +00:00
|
|
|
//Collections.sort(intervalIntersections);
|
2017-06-29 15:32:54 +00:00
|
|
|
|
2020-03-20 17:08:18 +00:00
|
|
|
r = n;
|
2020-03-24 07:04:07 +00:00
|
|
|
List<Point> intervalIntersections = new LinkedList<>(IntersectionComputer.getInstance().compute(setOfLines, interval.getLower(), interval.getUpper()));
|
2017-06-29 15:32:54 +00:00
|
|
|
while (true) {
|
2017-09-09 17:41:32 +00:00
|
|
|
if (this.N <= n || (Math.abs(interval.getUpper() - interval.getLower())) < EPSILON) {
|
2017-06-29 15:32:54 +00:00
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
//Anzahl der Schnittpunkte im Intervall [-Inf, a)
|
2020-03-24 07:04:07 +00:00
|
|
|
int numberOfIntersections = getIntervalSize(NEGATIV_INF, interval.getLower());
|
2017-06-29 15:32:54 +00:00
|
|
|
|
|
|
|
//Randomized Interpolating Search
|
|
|
|
j = (r / N) * (double) (k - numberOfIntersections);
|
2017-09-09 17:41:32 +00:00
|
|
|
jA = (int) Math.max(1, Math.floor(j - (1.5 * Math.sqrt(r))));
|
|
|
|
jB = (int) Math.min(r, Math.floor(j + (1.5 * 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
|
2020-03-24 07:04:07 +00:00
|
|
|
List<Point> sampledIntersections = RandomSampler.run(intervalIntersections, r);
|
2020-03-23 06:58:40 +00:00
|
|
|
|
|
|
|
aVariant = FastElementSelector.randomizedSelect(getIntersectionAbscissas(sampledIntersections), jA);
|
|
|
|
bVariant = FastElementSelector.randomizedSelect(getIntersectionAbscissas(sampledIntersections), jB);
|
2017-06-29 15:32:54 +00:00
|
|
|
} while (!checkCondition());
|
|
|
|
|
|
|
|
interval.setLower(aVariant);
|
|
|
|
interval.setUpper(bVariant);
|
2017-09-07 09:21:48 +00:00
|
|
|
intervalIntersections = getOpenIntervalElements(interval.getLower(), interval.getUpper());
|
2020-03-24 07:04:07 +00:00
|
|
|
N = getIntervalSize(interval.getLower(), interval.getUpper());
|
2017-06-29 15:32:54 +00:00
|
|
|
}
|
|
|
|
}
|
2019-08-01 05:19:04 +00:00
|
|
|
|
2020-03-21 19:54:03 +00:00
|
|
|
return pepareResult();
|
2017-06-29 15:32:54 +00:00
|
|
|
}
|
|
|
|
|
2020-03-23 06:58:40 +00:00
|
|
|
private List<Double> getIntersectionAbscissas(List<Point> interections) {
|
|
|
|
List<Double> abscissas = new ArrayList<>();
|
|
|
|
interections.forEach(e -> abscissas.add(e.getX()));
|
|
|
|
return abscissas;
|
|
|
|
}
|
|
|
|
|
2017-06-29 15:32:54 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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-08-03 06:27:38 +00:00
|
|
|
//Double kthElement = FastElementSelector.randomizedSelect(xCoordinates, k);
|
|
|
|
//Boolean cond1 = (kthElement > aVariant) && (kthElement <= bVariant);
|
2017-09-07 09:21:48 +00:00
|
|
|
|
2017-09-23 12:13:09 +00:00
|
|
|
int lowerCount = getIntervalSize(NEGATIV_INF, aVariant);
|
|
|
|
int higherCount = getIntervalSize(NEGATIV_INF, bVariant);
|
2017-09-07 09:21:48 +00:00
|
|
|
|
|
|
|
Boolean conda = k > lowerCount;
|
|
|
|
Boolean condb = k <= higherCount;
|
2017-08-03 06:27:38 +00:00
|
|
|
|
|
|
|
Boolean cond1 = conda && condb;
|
|
|
|
|
2017-09-10 15:45:47 +00:00
|
|
|
Boolean cond2 = (higherCount - lowerCount) <= ((11 * N) / Math.sqrt(r));
|
2017-06-29 21:22:34 +00:00
|
|
|
|
2017-09-10 15:45:47 +00:00
|
|
|
return (cond1 && cond2) || (aVariant == bVariant);
|
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)
|
|
|
|
*/
|
2017-09-23 12:13:09 +00:00
|
|
|
public int getIntervalSize(double a, double b) {
|
2020-03-24 07:04:07 +00:00
|
|
|
return getOpenIntervalElements(a,b).size();
|
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
|
2017-09-07 09:21:48 +00:00
|
|
|
* @return Liste der Schnittpunkte die im Interval (a,b) vertreten sind
|
2017-06-29 15:32:54 +00:00
|
|
|
*/
|
2020-03-21 00:37:09 +00:00
|
|
|
public List<Point> getOpenIntervalElements(double a, double b) {
|
2020-03-24 07:04:07 +00:00
|
|
|
Collection<Point> intersections = IntersectionComputer.getInstance().compute(setOfLines, a, b);
|
|
|
|
return new ArrayList<>(intersections);
|
2017-06-27 08:08:32 +00:00
|
|
|
}
|
|
|
|
|
2020-03-21 19:54:03 +00:00
|
|
|
private Line pepareResult() {
|
2017-08-02 05:40:08 +00:00
|
|
|
double m, x;
|
|
|
|
double b, y;
|
|
|
|
|
2020-03-21 00:37:09 +00:00
|
|
|
List<Point> resultSt = getOpenIntervalElements(interval.getLower(), interval.getUpper());
|
|
|
|
List<Double> resultAbscissas = new ArrayList<>();
|
2017-09-09 17:41:32 +00:00
|
|
|
|
2017-09-10 15:45:47 +00:00
|
|
|
for (Point p : resultSt) {
|
2017-09-09 17:41:32 +00:00
|
|
|
resultAbscissas.add(p.getX());
|
2017-08-02 05:40:08 +00:00
|
|
|
}
|
2017-06-29 15:32:54 +00:00
|
|
|
|
2020-03-23 06:58:40 +00:00
|
|
|
List<Double> yCoords = new ArrayList<>();
|
|
|
|
|
2020-03-24 07:04:07 +00:00
|
|
|
for (Point p : getOpenIntervalElements(interval.getLower(), interval.getUpper())) {
|
2020-03-23 06:58:40 +00:00
|
|
|
yCoords.add(p.getY());
|
2017-08-02 05:40:08 +00:00
|
|
|
}
|
2017-06-29 15:32:54 +00:00
|
|
|
|
2020-03-24 07:04:07 +00:00
|
|
|
double pseudoIndex = getIntervalSize(NEGATIV_INF, interval.getLower()) * 1.0;
|
2017-09-10 15:45:47 +00:00
|
|
|
m = FastElementSelector.randomizedSelect(resultAbscissas, k - pseudoIndex);
|
2017-06-29 15:32:54 +00:00
|
|
|
|
2020-03-23 06:58:40 +00:00
|
|
|
Set<Double> unique = new LinkedHashSet<>(yCoords);
|
|
|
|
yCoords.clear();
|
|
|
|
yCoords.addAll(unique);
|
|
|
|
b = FastElementSelector.randomizedSelect(yCoords, yCoords.size() * 0.5) * -1;
|
2017-08-02 05:40:08 +00:00
|
|
|
slope = m;
|
|
|
|
yInterception = b;
|
2017-06-29 15:32:54 +00:00
|
|
|
|
2020-03-21 00:37:09 +00:00
|
|
|
if (this.subscriber != null) {
|
|
|
|
AlgorithmData data = new AlgorithmData();
|
|
|
|
data.setType(SubscriberType.TS);
|
|
|
|
data.setLineData(new Line(m, b));
|
|
|
|
this.subscriber.onNext(data);
|
2017-06-29 15:32:54 +00:00
|
|
|
}
|
2020-03-21 19:54:03 +00:00
|
|
|
|
|
|
|
return new Line(getSlope(), getYInterception());
|
2017-06-29 11:10:15 +00:00
|
|
|
}
|
2017-06-29 15:32:54 +00:00
|
|
|
|
2017-10-15 13:21:53 +00:00
|
|
|
/**
|
|
|
|
* @return Steigung
|
|
|
|
*/
|
2017-08-01 19:59:33 +00:00
|
|
|
public Double getSlope() {
|
|
|
|
return slope;
|
|
|
|
}
|
|
|
|
|
2017-10-15 13:21:53 +00:00
|
|
|
/**
|
|
|
|
* @return y-Achsenabschnitt
|
|
|
|
*/
|
2020-03-21 19:54:03 +00:00
|
|
|
public Double getYInterception() {
|
2017-08-01 19:59:33 +00:00
|
|
|
return yInterception;
|
|
|
|
}
|
2020-03-21 00:37:09 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void subscribe(Flow.Subscriber<? super Data> subscriber) {
|
|
|
|
this.subscriber = subscriber;
|
|
|
|
}
|
2017-05-28 12:00:01 +00:00
|
|
|
}
|