223 lines
8.1 KiB
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;
|
|
}
|
|
}
|