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 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 subscriber; private Map parameter; private List 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 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 lines) { this.setOfLines = new LinkedList<>(lines); } @Override public Type getType() { return type; } @Override public void setPresenter(AbstractPresenter presenter) { subscribe(presenter); } private List getIntersectionAbscissas(List interections) { List 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 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 a und * b 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 a und * b 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 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 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 subscriber) { this.subscriber = subscriber; } @Override public Map getParameter() { return this.parameter; } @Override public void setParameter(Map parameter) { this.parameter = parameter; } }