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

223 lines
8.1 KiB
Java

package de.wwwu.awolf.presenter.algorithms.advanced;
import de.wwwu.awolf.model.dao.Interval;
import de.wwwu.awolf.model.dao.Line;
import de.wwwu.awolf.model.dao.Point;
import de.wwwu.awolf.model.communication.AlgorithmMessage;
import de.wwwu.awolf.model.communication.Message;
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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Flow;
import java.util.stream.Collectors;
/**
* 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 List<Line> setOfLines;
private double r;
private double intervalSize;
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 AlgorithmMessage> subscriber;
private Map<String, String> parameter;
private List<Point> intersections;
/**
* 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() {
int numberOfLines = this.setOfLines.size();
this.intervalSize = BinomialCoeffizient.run(numberOfLines, 2);
//this.k = Integer.valueOf((int) (N * 0.5)) - 1;
this.k = (int) (intervalSize / 2);
interval = new Interval(Double.MIN_VALUE, Double.MAX_VALUE);
//damit eine initiale Ordnung herscht
r = numberOfLines;
intersections = new LinkedList<>(IntersectionComputer.getInstance().compute(setOfLines));
while (true) {
double EPSILON = 0.00001;
if (this.intervalSize <= numberOfLines || (Math.abs(interval.getUpper() - interval.getLower())) < EPSILON) {
break;
} else {
//Anzahl der Schnittpunkte im Intervall [-Inf, a)
int numberOfIntersections = getIntervalSize(Double.MIN_VALUE, interval.getLower());
//Randomized Interpolating Search
//Hilfsvariablen (siehe original Paper)
double j = (r / intervalSize) * (double) (k - numberOfIntersections);
int jA = (int) Math.max(1, Math.floor(j - (1.5 * Math.sqrt(r))));
int 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 */
List<Point> sampledIntersections = new LinkedList<>();
do {
//zufällige Stichprobe
Collections.shuffle(intersections);
for(int i=0; i < r; i++) {
sampledIntersections.add(intersections.get(i % intersections.size()));
}
aVariant = FastElementSelector.randomizedSelect(getIntersectionAbscissas(sampledIntersections), jA);
bVariant = FastElementSelector.randomizedSelect(getIntersectionAbscissas(sampledIntersections), jB);
} while (!checkCondition(sampledIntersections));
interval.setLower(aVariant);
interval.setUpper(bVariant);
intersections = getOpenIntervalElements(interval.getLower(), interval.getUpper());
intervalSize = getIntervalSize(interval.getLower(), interval.getUpper());
}
}
return prepareResults();
}
@Override
public void setInput(Set<Line> lines) {
this.setOfLines = new LinkedList<>(lines);
}
@Override
public Type getType() {
return type;
}
@Override
public void setPresenter(AbstractPresenter 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(List<Point> sampledIntersections) {
Point kthElement = FastElementSelector.randomizedSelect(sampledIntersections, k);
Boolean cond1 = (kthElement.getX() > aVariant) && (kthElement.getX() <= bVariant);
int count = getIntervalSize(aVariant, bVariant);
Boolean cond2 = count <= ((11 * intervalSize) / 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) {
if (a == b)
return 0;
else
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) {
return intersections.stream().filter(point -> point.getX().compareTo(a) >= 0 && point.getX() < 0).collect(Collectors.toList());
}
private Line prepareResults() {
List<Point> intervalElements = getOpenIntervalElements(interval.getLower(), interval.getUpper());
double pseudoIndex = getIntervalSize(-9999.0, interval.getLower()) * 1.0;
double m = FastElementSelector.randomizedSelect(intervalElements, k - pseudoIndex).getX();
double b = FastElementSelector.randomizedSelect(intervalElements, intervalElements.size() * 0.5).getY() * -1;
slope = m * -1;
yInterception = b;
if (this.subscriber != null) {
AlgorithmMessage data = new AlgorithmMessage();
data.setAlgorithmType(getType());
data.setType(SubscriberType.ALGORITHM);
data.setLineData(new Line(slope, yInterception));
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 Message> subscriber) {
this.subscriber = subscriber;
}
@Override
public Map<String, String> getParameter() {
return this.parameter;
}
@Override
public void setParameter(Map<String, String> parameter) {
this.parameter = parameter;
}
}