algorithms-for-computing-li.../LinearRegressionTool/src/main/java/de/wwwu/awolf/presenter/algorithms/advanced/TheilSenEstimator.java

237 lines
8.1 KiB
Java

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.AbstractPresenter;
import de.wwwu.awolf.presenter.algorithms.Algorithm;
import de.wwwu.awolf.presenter.util.BinomialCoeffizient;
import de.wwwu.awolf.presenter.util.FastElementSelector;
import de.wwwu.awolf.presenter.util.IntersectionComputer;
import de.wwwu.awolf.presenter.util.RandomSampler;
import java.util.*;
import java.util.concurrent.Flow;
/**
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
*
* @Author: Armin Wolf
* @Email: a_wolf28@uni-muenster.de
* @Date: 28.05.2017.
*/
public class TheilSenEstimator implements Algorithm {
private static final Algorithm.Type type = Type.TS;
private final double POSITIV_INF = 9999.0;
private final double NEGATIV_INF = -9999.0;
private final double EPSILON = 0.00001;
private List<Line> setOfLines;
//Hilfsvariablen (siehe original Paper)
private double j;
private int jA;
private int jB;
private double r;
private int n;
private double N;
private int k;
//Intervall und die temporaeren Grenzen
private Interval interval;
private double aVariant;
private double bVariant;
private double slope;
private double yInterception;
private Flow.Subscriber<? super AlgorithmData> subscriber;
private AbstractPresenter presenter;
/**
* 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 Line call() {
this.n = this.setOfLines.size();
this.N = BinomialCoeffizient.run(n, 2);
//this.k = Integer.valueOf((int) (N * 0.5)) - 1;
this.k = (int) (N / 2);
interval = new Interval(NEGATIV_INF, POSITIV_INF);
//damit eine initiale Ordnung herscht
//Collections.sort(intervalIntersections);
r = n;
List<Point> intervalIntersections = new LinkedList<>(IntersectionComputer.getInstance().compute(setOfLines, interval.getLower(), interval.getUpper()));
while (true) {
if (this.N <= n || (Math.abs(interval.getUpper() - interval.getLower())) < EPSILON) {
break;
} else {
//Anzahl der Schnittpunkte im Intervall [-Inf, a)
int numberOfIntersections = getIntervalSize(NEGATIV_INF, interval.getLower());
//Randomized Interpolating Search
j = (r / N) * (double) (k - numberOfIntersections);
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))));
/* 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
List<Point> sampledIntersections = RandomSampler.run(intervalIntersections, r);
aVariant = FastElementSelector.randomizedSelect(getIntersectionAbscissas(sampledIntersections), jA);
bVariant = FastElementSelector.randomizedSelect(getIntersectionAbscissas(sampledIntersections), jB);
} while (!checkCondition());
interval.setLower(aVariant);
interval.setUpper(bVariant);
intervalIntersections = getOpenIntervalElements(interval.getLower(), interval.getUpper());
N = getIntervalSize(interval.getLower(), interval.getUpper());
}
}
return pepareResult();
}
@Override
public void setInput(Set<Line> lines) {
this.setOfLines = new LinkedList<>(lines);
}
@Override
public Type getType() {
return type;
}
@Override
public void setPresenter(AbstractPresenter presenter) {
this.presenter = presenter;
subscribe(presenter);
}
private List<Double> getIntersectionAbscissas(List<Point> interections) {
List<Double> abscissas = new ArrayList<>();
interections.forEach(e -> abscissas.add(e.getX()));
return abscissas;
}
/**
* 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() {
//Double kthElement = FastElementSelector.randomizedSelect(xCoordinates, k);
//Boolean cond1 = (kthElement > aVariant) && (kthElement <= bVariant);
int lowerCount = getIntervalSize(NEGATIV_INF, aVariant);
int higherCount = getIntervalSize(NEGATIV_INF, bVariant);
Boolean conda = k > lowerCount;
Boolean condb = k <= higherCount;
Boolean cond1 = conda && condb;
Boolean cond2 = (higherCount - lowerCount) <= ((11 * N) / Math.sqrt(r));
return (cond1 && cond2) || (aVariant == bVariant);
}
/**
* 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) {
return getOpenIntervalElements(a, b).size();
}
/**
* 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 List<Point> getOpenIntervalElements(double a, double b) {
Collection<Point> intersections = IntersectionComputer.getInstance().compute(setOfLines, a, b);
return new ArrayList<>(intersections);
}
private Line pepareResult() {
double m, x;
double b, y;
List<Point> resultSt = getOpenIntervalElements(interval.getLower(), interval.getUpper());
List<Double> resultAbscissas = new ArrayList<>();
for (Point p : resultSt) {
resultAbscissas.add(p.getX());
}
List<Double> yCoords = new ArrayList<>();
for (Point p : getOpenIntervalElements(interval.getLower(), interval.getUpper())) {
yCoords.add(p.getY());
}
double pseudoIndex = getIntervalSize(NEGATIV_INF, interval.getLower()) * 1.0;
m = FastElementSelector.randomizedSelect(resultAbscissas, k - pseudoIndex);
Set<Double> unique = new LinkedHashSet<>(yCoords);
yCoords.clear();
yCoords.addAll(unique);
b = FastElementSelector.randomizedSelect(yCoords, yCoords.size() * 0.5) * -1;
slope = m;
yInterception = b;
if (this.subscriber != null) {
AlgorithmData data = new AlgorithmData();
data.setAlgorithmType(getType());
data.setType(SubscriberType.ALGORITHM);
data.setLineData(new Line(m, b));
this.subscriber.onNext(data);
}
return new Line(getSlope(), getYInterception());
}
/**
* @return Steigung
*/
public Double getSlope() {
return slope;
}
/**
* @return y-Achsenabschnitt
*/
public Double getYInterception() {
return yInterception;
}
@Override
public void subscribe(Flow.Subscriber<? super Data> subscriber) {
this.subscriber = subscriber;
}
}