369 lines
11 KiB
Java
369 lines
11 KiB
Java
package de.wwwu.awolf.presenter.algorithms.advanced;
|
||
|
||
import de.wwwu.awolf.model.Interval;
|
||
import de.wwwu.awolf.model.Line;
|
||
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.Presenter;
|
||
import de.wwwu.awolf.presenter.algorithms.Algorithm;
|
||
import de.wwwu.awolf.presenter.util.FastElementSelector;
|
||
import de.wwwu.awolf.presenter.util.IntersectionCounter;
|
||
import de.wwwu.awolf.presenter.util.Logging;
|
||
import de.wwwu.awolf.presenter.util.RandomSampler;
|
||
|
||
import java.util.ArrayList;
|
||
import java.util.HashMap;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
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, Flow.Publisher<Data> {
|
||
|
||
private Presenter presenter;
|
||
private List<Line> set;
|
||
private Map<Line, Double> medianIntersections = new HashMap<>();
|
||
private Map<Line, ArrayList<Double>> intersectionAbscissas = new HashMap<>();
|
||
private Interval interval;
|
||
|
||
//in der Literatur als L_i, C_i, und R_i bekannt
|
||
private List<Double> countLeftSlab;
|
||
private List<Double> countCenterSlab;
|
||
private List<Double> countRightSlab;
|
||
|
||
//die Mengen L,C und R
|
||
private List<Line> linesInLeftSlab;
|
||
private List<Line> linesInCenterSlab;
|
||
private List<Line> 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;
|
||
private Flow.Subscriber<? super AlgorithmData> subscriber;
|
||
|
||
/**
|
||
* Konstruktor
|
||
*
|
||
* @param set Liste der Geraden
|
||
* @param presenter Presenter (Beobachter)
|
||
*/
|
||
public RepeatedMedianEstimator(List<Line> 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<>();
|
||
subscribe(presenter);
|
||
}
|
||
|
||
/**
|
||
* Konstruktor
|
||
*
|
||
* @param set Liste der Geraden
|
||
*/
|
||
public RepeatedMedianEstimator(List<Line> set) {
|
||
this(set, null);
|
||
}
|
||
|
||
/**
|
||
* Führt den Algortihmus zur Berechnung des RM-Schätzers durch.
|
||
* <p>
|
||
* 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() {
|
||
|
||
Logging.logInfo("=== 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));
|
||
List<Line> lines = RandomSampler.run(linesInCenterSlab, r, linesInCenterSlab.size());
|
||
|
||
//Für jede Gerade aus der Stichprobe wird der Schnittpunkt mit der medianen
|
||
//x-Koordinate bestimmt
|
||
List<Double> 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();
|
||
Logging.logInfo("Zeit: " + ((end - start) / 1000));
|
||
|
||
return 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) {
|
||
|
||
int index = Integer.parseInt(sampledLine.getId());
|
||
IntersectionCounter intersectionCounter = new IntersectionCounter();
|
||
List<Double> intersections = intersectionCounter.calculateIntersectionAbscissas(linesInCenterSlab, sampledLine);
|
||
|
||
double ki = Math.ceil((n - 1) * 0.5) - 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)) * (0.5))));
|
||
kHigh = Math.min(r, Math.floor(((r * k) / (linesInCenterSlab.size()))
|
||
+ ((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() {
|
||
for (Line line : linesInCenterSlab) {
|
||
List<Double> intersections = intersectionAbscissas.get(line);
|
||
int 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);
|
||
}
|
||
|
||
}
|
||
|
||
/**
|
||
* TODO bad idea!!!
|
||
* 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 * 0.5) && Math.ceil(n * 0.5) <= linesInLeftSlab.size() + linesInCenterSlab.size()) {
|
||
interval.setLower(thetaLow + 0.1);
|
||
interval.setUpper(thetaHigh);
|
||
}
|
||
// wähle L als C
|
||
else if (Math.ceil(n * 0.5) <= linesInLeftSlab.size()) {
|
||
interval.setUpper(thetaLow);
|
||
}
|
||
//wähle R als C
|
||
else if (linesInLeftSlab.size() + linesInCenterSlab.size() < Math.ceil(n * 0.5) && Math.ceil(n * 0.5) <= (linesInLeftSlab.size() + linesInCenterSlab.size() + linesInRightSlab.size())) {
|
||
interval.setLower(thetaHigh - 0.1);
|
||
}
|
||
}
|
||
|
||
private Line pepareResult() {
|
||
if (this.subscriber != null) {
|
||
double m = thetaLow;
|
||
double b = (-1) * (
|
||
(linesInCenterSlab.get(0).getM() * (thetaLow)) + linesInCenterSlab.get(0)
|
||
.getB());
|
||
|
||
slope = m;
|
||
yInterception = b;
|
||
AlgorithmData data = new AlgorithmData();
|
||
data.setType(SubscriberType.RM);
|
||
data.setLineData(new Line(m, b));
|
||
this.subscriber.onNext(data);
|
||
} else {
|
||
double m = thetaLow;
|
||
double b = (-1) * ((linesInCenterSlab.get(0).getM() * (thetaLow)) + linesInCenterSlab.get(0).getB());
|
||
slope = m;
|
||
yInterception = b;
|
||
}
|
||
|
||
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 List<Double> getCountLeftSlab() {
|
||
return countLeftSlab;
|
||
}
|
||
|
||
/**
|
||
* @return verteilung der Punkte
|
||
*/
|
||
public List<Double> getCountCenterSlab() {
|
||
return countCenterSlab;
|
||
}
|
||
|
||
/**
|
||
* @return verteilung der Punkte
|
||
*/
|
||
public List<Double> getCountRightSlab() {
|
||
return countRightSlab;
|
||
}
|
||
|
||
@Override
|
||
public void subscribe(Flow.Subscriber<? super Data> subscriber) {
|
||
this.subscriber = subscriber;
|
||
}
|
||
}
|
||
|
||
|