algorithms-for-computing-li.../LinearRegressionTool/src/main/java/de/wwwu/awolf/presenter/algorithms/advanced/RepeatedMedianEstimator.java

368 lines
11 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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. 136150
*/
public void run() {
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));
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);
}
}
@Override
public void 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 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;
}
}