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.FastElementSelector; import de.wwwu.awolf.presenter.util.IntersectionComputer; import de.wwwu.awolf.presenter.util.Logging; 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 RepeatedMedianEstimator implements Algorithm { private static final Algorithm.Type type = Type.RM; private AbstractPresenter presenter; private List setOfLines; private Interval interval; private Interval original; //in der Literatur als L_i, C_i, und R_i bekannt private int countLeftSlab; private int countCenterSlab; private int countRightSlab; //in der Literatur als L_i, C_i, und R_i bekannt private Set intersectionsInLeftSlab; private Set intersectionsInCenterSlab; private Set intersectionsInRightSlab; private double r; private int n; private double k; private double kLow; private double kHigh; private double beta; private double slope; private double yInterception; private Flow.Subscriber subscriber; /** * Führt den Algortihmus zur Berechnung des RM-Schätzers durch. *

* Paper: * Matousek, Jiri, D. M. Mount und N. S. Netanyahu * „Efficient Randomized Algorithms for the Repeated Median Line Estimator“. 1998 * Algorithmica 20.2, S. 136–150 */ public Line call() { n = setOfLines.size(); interval = new Interval(-10000, 10000); original = new Interval(-10000, 10000); beta = 0.5; intersectionsInLeftSlab = new HashSet<>(); intersectionsInCenterSlab = new HashSet<>(); intersectionsInRightSlab = new HashSet<>(); intersectionsInLeftSlab.add(new Point(0d, 0d)); intersectionsInCenterSlab.add(new Point(0d, 0d)); intersectionsInCenterSlab.add(new Point(0d, 0d)); for (int i = 0; i < n; i++) { countLeftSlab = 0; countRightSlab = 0; countCenterSlab = setOfLines.size(); } Logging.logInfo("=== S T A R T - R M ==="); long start; long end; start = System.currentTimeMillis(); double thetaLow = 0; double thetaHigh = 0; while (countCenterSlab > 1) { n = countCenterSlab; r = Math.ceil(Math.pow(n, beta)); List lines = RandomSampler.run(setOfLines, r); //Für jede Gerade aus der Stichprobe wird der Schnittpunkt mit der medianen //x-Koordinate bestimmt List medianIntersectionAbscissas = new ArrayList<>(); final double lowerBound = thetaLow; final double upperBound = thetaHigh; if (!lines.isEmpty()) { lines.forEach(line -> medianIntersectionAbscissas.add(estimateMedianIntersectionAbscissas(lines, line))); } //Rang vom RM-Wert in C k = Math.max(1, Math.min(n, (Math.ceil(n * 0.5) - countLeftSlab))); //berechne k_lo und k_hi computeSlabBorders(); //Berechne die Elemente mit dem Rang Theta_lo und Theta_hi Collections.sort(medianIntersectionAbscissas); thetaLow = FastElementSelector.randomizedSelect(medianIntersectionAbscissas, kLow); thetaHigh = FastElementSelector.randomizedSelect(medianIntersectionAbscissas, kHigh); //Für jede Gerade in C wird die Anzahl der Schnittpunkte die im Intervall liegen hochgezählt countNumberOfIntersectionsAbscissas(thetaLow, thetaHigh); //verkleinere das Intervall contractIntervals(thetaLow, thetaHigh); } end = System.currentTimeMillis(); Logging.logInfo("=== E N D - R M === " + ((end - start) / 1000)); return pepareResult(thetaLow, thetaHigh); } @Override public void setInput(Set lines) { this.setOfLines = new LinkedList<>(lines); this.n = setOfLines.size(); } @Override public Type getType() { return type; } @Override public void setPresenter(AbstractPresenter presenter) { this.presenter = presenter; subscribe(presenter); } /** * Berechnet die mediane x-Koordinate über den Schnittpunkten. * * @param sampledLine Stichprobe von Geraden * @return mediane x-Koordinate über den Schnittpunkten */ public Double estimateMedianIntersectionAbscissas(List lines, Line sampledLine) { List intersections = IntersectionComputer.getInstance().calculateIntersectionAbscissas(lines, sampledLine, original.getLower(), original.getUpper()); List left = IntersectionComputer.getInstance().calculateIntersectionAbscissas(lines, sampledLine, original.getLower(), interval.getLower()); List center = IntersectionComputer.getInstance().calculateIntersectionAbscissas(lines, sampledLine, interval.getLower(), interval.getUpper()); double ki = Math.ceil((n - 1) * 0.5) - left.size(); double i = (Math.ceil((Math.sqrt(n) * ki) / center.size())); int accessIndex; if (i < 0) accessIndex = 0; else if (i >= intersections.size() && !intersections.isEmpty()) accessIndex = intersections.size() - 1; else accessIndex = (int) i; return FastElementSelector.randomizedSelect(intersections, accessIndex); } /** * Berechnet die potenziell neuen Intervallgrenzen. */ public void computeSlabBorders() { kLow = Math.max(1, Math.floor(((r * k) / (countCenterSlab)) - ((3 * Math.sqrt(r)) * (0.5)))); kHigh = Math.min(r, Math.floor(((r * k) / (countCenterSlab)) + ((3 * Math.sqrt(r)) * (0.5)))); } /** * Berechnet die Anzahl der Schnittpunkte pro Bereich. Insgesammt gibt es drei Bereiche: * Im Intervall => (a,b], vor dem Intervall => (a', a], hinter dem Intervall => (b, b']. */ public void countNumberOfIntersectionsAbscissas(final double lower, final double upper) { IntersectionComputer instance = IntersectionComputer.getInstance(); intersectionsInLeftSlab = new HashSet<>(instance.compute(setOfLines, interval.getLower(), lower)); intersectionsInCenterSlab = new HashSet<>(instance.compute(setOfLines, lower, upper)); intersectionsInRightSlab = new HashSet<>(instance.compute(setOfLines, upper, interval.getUpper())); int tmp = new HashSet<>(instance.compute(setOfLines, interval.getLower(), interval.getUpper())).size(); countLeftSlab = intersectionsInLeftSlab.size(); countCenterSlab = intersectionsInCenterSlab.size(); countRightSlab = intersectionsInRightSlab.size(); } /** * Verkleinert das aktuelle Intervall. Eines der drei Bereiche wird als neues Intervall gewählt. * Auf diesem Intervall werden dann in der nächsten Iteration wieder drei Bereiche bestimmt. */ public void contractIntervals(final double lower, final double upper) { double max = Math.max(countLeftSlab, Math.max(countCenterSlab, countRightSlab)); boolean newIntervalIsC = countLeftSlab < Math.ceil(n * 0.5) && Math.ceil(n * 0.5) <= countLeftSlab + countCenterSlab; boolean newIntervalIsL = Math.ceil(n * 0.5) <= countLeftSlab; boolean newIntervalIsR = countLeftSlab + countCenterSlab < Math.ceil(n * 0.5) && Math.ceil(n * 0.5) <= (countLeftSlab + countCenterSlab + countRightSlab); //wähle C als C if (newIntervalIsC) { interval.setLower(lower); interval.setUpper(upper); } else if (newIntervalIsL) { interval.setUpper(lower); } else if (newIntervalIsR) { interval.setLower(upper); } } private Line pepareResult(final double thetaLow, final double thetaHigh) { slope = thetaLow; List potentialYInterceptions = new ArrayList<>(); setOfLines.forEach(line -> { potentialYInterceptions.add(line.getB() - (slope * line.getM())); }); yInterception = FastElementSelector.randomizedSelect(potentialYInterceptions, Math.floor(potentialYInterceptions.size() * 0.5)); if (this.subscriber != null) { AlgorithmData data = new AlgorithmData(); data.setAlgorithmType(getType()); data.setType(SubscriberType.ALGORITHM); data.setLineData(new Line(getSlope(), getyInterception())); this.subscriber.onNext(data); } return new Line(getSlope(), getyInterception()); } /** * @return Anzahl der Geraden */ public Integer getN() { return n; } /** * @param n Anzahl der Geraden */ public void setN(Integer n) { this.n = n; } /** * @param beta Parameter Beta */ public void setBeta(Double beta) { this.beta = beta; } /** * @return Steigung */ public Double getSlope() { return slope; } /** * @return y-Achsenabschnitt */ public Double getyInterception() { return yInterception; } /** * @return temporäres untere Intervallgrenze */ public Double getkLow() { return kLow; } /** * @param kLow temporäres untere Intervallgrenze */ public void setkLow(Double kLow) { this.kLow = kLow; } /** * @return temporäres oberes Intervallgrenze */ public Double getkHigh() { return kHigh; } /** * @param kHigh temporäres oberes Intervallgrenze */ public void setkHigh(Double kHigh) { this.kHigh = kHigh; } /** * @return verteilung der Punkte */ public int getCountLeftSlab() { return countLeftSlab; } /** * @return verteilung der Punkte */ public int getCountCenterSlab() { return countCenterSlab; } /** * @return verteilung der Punkte */ public int getCountRightSlab() { return countRightSlab; } @Override public void subscribe(Flow.Subscriber subscriber) { this.subscriber = subscriber; } }