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.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.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; 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 List setOfLines; private Interval interval; private Interval original; //in der Literatur als L_i, C_i, und R_i bekannt private long countLeftSlab; private long countCenterSlab; private long 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 long n; private double k; private double kLow; private double kHigh; private double slope; private double yInterception; private Flow.Subscriber subscriber; private Map parameter; private Set intersections; public RepeatedMedianEstimator() { parameter = new HashMap<>(); parameter.put("Beta", String.valueOf(0.5)); } /** * 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); 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; intersections = IntersectionComputer.getInstance().compute(setOfLines); while (countCenterSlab > 1) { n = countCenterSlab; r = Math.ceil(Math.pow(n, Double.parseDouble(parameter.get("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) { 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) { //interval is the current interval Set intersections = IntersectionComputer.getInstance().compute(lines); long leftSize = intersections.stream().filter(p -> p.getX() <= interval.getLower()).count(); long centerSize = intersections.stream().filter(p -> p.getX() >= interval.getLower() && p.getX() < interval.getUpper()).count(); double ki = Math.ceil((n - 1) * 0.5) - leftSize; double i = (Math.ceil((Math.sqrt(n) * ki) / centerSize)); int accessIndex; if (i < 0) { accessIndex = 0; } else if (i >= intersections.size() && !intersections.isEmpty()) { accessIndex = intersections.size() - 1; } else { accessIndex = (int) i; } LinkedList points = new LinkedList<>(intersections); return FastElementSelector.randomizedSelect(points, accessIndex).getX(); } /** * 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) { countLeftSlab = intersections.stream().filter(point -> point.getX().compareTo(lower) < 0).count(); countCenterSlab = intersections.stream().filter(point -> point.getX().compareTo(lower) >= 0 && point.getX().compareTo(upper) < 0).count(); countRightSlab = intersections.stream().filter(point -> point.getX().compareTo(upper) >= 0).count(); } /** * 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 * -1; 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) { AlgorithmMessage data = new AlgorithmMessage(); 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 Long getN() { return n; } /** * @param n Anzahl der Geraden */ public void setN(Integer n) { this.n = n; } /** * @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; } }