package presenter.algorithms.advanced; import model.Interval; import model.Line; import presenter.Presenter; import presenter.algorithms.Algorithm; import presenter.util.FastElementSelector; import presenter.util.IntersectionCounter; import presenter.util.RandomSampler; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.Observable; /** * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden. * * @Author: Armin Wolf * @Email: a_wolf28@uni-muenster.de * @Date: 28.05.2017. */ public class RepeatedMedianEstimator extends Observable implements Algorithm { private Presenter presenter; private LinkedList set; private HashMap> linePairs; private HashMap medianIntersections = new HashMap<>(); private HashMap> intersectionAbscissas = new HashMap<>(); private Interval interval; //in der Literatur als L_i, C_i, und R_i bekannt private ArrayList countLeftSlab; private ArrayList countCenterSlab; private ArrayList countRightSlab; //die Mengen L,C und R private ArrayList linesInLeftSlab; private ArrayList linesInCenterSlab; private ArrayList linesInRightSlab; private double r; private int n; private double k; private double kLow; private double kHigh; private double beta; private double thetaLow; private double thetaHigh; private double slope; private double yInterception; /** * Konstruktor * @param set Liste der Geraden * @param presenter Presenter (Beobachter) */ public RepeatedMedianEstimator(LinkedList set, Presenter presenter) { this.set = set; this.presenter = presenter; interval = new Interval(-10000, 10000); n = set.size(); beta = 0.5; countLeftSlab = new ArrayList<>(); countCenterSlab = new ArrayList<>(); countRightSlab = new ArrayList<>(); for (int i = 0; i < n; i++) { countLeftSlab.add(0d); countRightSlab.add(0d); countCenterSlab.add(n - 1.0); intersectionAbscissas.put(set.get(i), new ArrayList<>()); } linesInLeftSlab = new ArrayList<>(); linesInCenterSlab = new ArrayList<>(set); linesInRightSlab = new ArrayList<>(); linePairs = new HashMap<>(); } /** * Konstruktor * @param set Liste der Geraden */ public RepeatedMedianEstimator(LinkedList set) { this(set, null); } /** * 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 void run() { System.out.println("=== S T A R T - R M ==="); long start; long end; start = System.currentTimeMillis(); while (linesInCenterSlab.size() != 1) { n = linesInCenterSlab.size(); r = Math.ceil(Math.pow(n, beta)); ArrayList lines = RandomSampler.run(linesInCenterSlab, r, linesInCenterSlab.size()); //Für jede Gerade aus der Stichprobe wird der Schnittpunkt mit der medianen //x-Koordinate bestimmt ArrayList medianIntersectionAbscissas = new ArrayList<>(); for (Line l : lines) { Double abscissa = estimateMedianIntersectionAbscissas(l); medianIntersections.put(l, abscissa); medianIntersectionAbscissas.add(abscissa); } //Rang vom RM-Wert in C k = Math.max(1, Math.min(set.size(), (Math.ceil(n * 0.5) - linesInLeftSlab.size()))); //berechne k_lo und k_hi computeSlabBorders(); //Berechne die Elemente mit dem Rang Theta_lo und Theta_hi 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(); //verkleinere das Intervall contractIntervals(); } end = System.currentTimeMillis(); System.out.println("Zeit: "+ ((end-start)/1000)); pepareResult(); } /** * Berechnet die mediane x-Koordinate über den Schnittpunkten. * * @param sampledLine Stichprobe von Geraden * @return mediane x-Koordinate über den Schnittpunkten */ public Double estimateMedianIntersectionAbscissas(Line sampledLine) { Integer index = Integer.parseInt(sampledLine.getId()); IntersectionCounter intersectionCounter = new IntersectionCounter(); ArrayList intersections = intersectionCounter.calculateIntersectionAbscissas(linesInCenterSlab, sampledLine); double ki = Math.ceil((n - 1) / 2) - FastElementSelector.randomizedSelect(countLeftSlab, index); double i = (Math.ceil((Math.sqrt(n) * ki) / FastElementSelector.randomizedSelect(countCenterSlab, index))); int accessIndex; if (i < 0) accessIndex = 0; else if (i >= intersections.size()) 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) / (linesInCenterSlab.size())) - ((3 * Math.sqrt(r)) / (2)))); kHigh = Math.min(r, Math.floor(((r * k) / (linesInCenterSlab.size())) + ((3 * Math.sqrt(r)) / (2)))); } /** * 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() { for (Line line : linesInCenterSlab) { ArrayList intersections = intersectionAbscissas.get(line); Integer index = Integer.parseInt(line.getId()); int left = 0; int center = 0; int right = 0; for (Double intersection : intersections) { if (intersection <= thetaLow) { left++; } else if (intersection > thetaLow && intersection <= thetaHigh) { center++; } else if (intersection > thetaHigh) { right++; } } countLeftSlab.set(index, (double) left); countCenterSlab.set(index, (double) center); countRightSlab.set(index, (double) right); } } /** * 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() { for (int i = 0; i < linesInCenterSlab.size(); i++) { double left = countLeftSlab.get(i); double center = countCenterSlab.get(i); double right = countRightSlab.get(i); double max = Math.max(left, Math.max(center, right)); if (left == max) { linesInLeftSlab.add(linesInCenterSlab.get(i)); linesInCenterSlab.remove(i); } else if (right == max) { linesInRightSlab.add(linesInCenterSlab.get(i)); linesInCenterSlab.remove(i); } } //wähle C als C if (linesInLeftSlab.size() < Math.ceil(n / 2) && Math.ceil(n / 2) <= linesInLeftSlab.size() + linesInCenterSlab.size()) { interval.setLower(thetaLow + 0.1); interval.setUpper(thetaHigh); } // wähle L als C else if (Math.ceil(n / 2) <= linesInLeftSlab.size()) { interval.setUpper(thetaLow); } //wähle R als C else if (linesInLeftSlab.size() + linesInCenterSlab.size() < Math.ceil(n / 2) && Math.ceil(n / 2) <= (linesInLeftSlab.size() + linesInCenterSlab.size() + linesInRightSlab.size())) { interval.setLower(thetaHigh - 0.1); } } @Override public void pepareResult() { if (presenter != null) { setChanged(); double m = thetaLow; double b = (-1) * ( (linesInCenterSlab.get(0).getM() * (thetaLow)) + linesInCenterSlab.get(0) .getB()); slope = m; yInterception = b; String[] result = new String[]{"rm", m + "", b + ""}; notifyObservers(result); } else { double m = thetaLow; double b = (-1) * ((linesInCenterSlab.get(0).getM() * (thetaLow)) + linesInCenterSlab.get(0).getB()); slope = m; yInterception = b; } } /******************************************************************************************************************* * Getter und Setter Methoden ******************************************************************************************************************/ /** * @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 ArrayList getCountLeftSlab() { return countLeftSlab; } /** * @return verteilung der Punkte */ public ArrayList getCountCenterSlab() { return countCenterSlab; } /** * @return verteilung der Punkte */ public ArrayList getCountRightSlab() { return countRightSlab; } }