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

357 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 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<Line> set;
private HashMap<Line, ArrayList<Line>> linePairs;
private HashMap<Line, Double> medianIntersections = new HashMap<>();
private HashMap<Line, ArrayList<Double>> intersectionAbscissas = new HashMap<>();
private Interval interval;
//in der Literatur als L_i, C_i, und R_i bekannt
private ArrayList<Double> countLeftSlab;
private ArrayList<Double> countCenterSlab;
private ArrayList<Double> countRightSlab;
//die Mengen L,C und R
private ArrayList<Line> linesInLeftSlab;
private ArrayList<Line> linesInCenterSlab;
private ArrayList<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;
/**
* Konstruktor
* @param set Liste der Geraden
* @param presenter Presenter (Beobachter)
*/
public RepeatedMedianEstimator(LinkedList<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<>();
linePairs = new HashMap<>();
}
/**
* Konstruktor
* @param set Liste der Geraden
*/
public RepeatedMedianEstimator(LinkedList<Line> 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. 136150
*/
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<Line> lines = RandomSampler.run(linesInCenterSlab, r, linesInCenterSlab.size());
//Für jede Gerade aus der Stichprobe wird der Schnittpunkt mit der medianen
//x-Koordinate bestimmt
ArrayList<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();
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<Double> 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<Double> 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<Double> getCountLeftSlab() {
return countLeftSlab;
}
/**
* @return verteilung der Punkte
*/
public ArrayList<Double> getCountCenterSlab() {
return countCenterSlab;
}
/**
* @return verteilung der Punkte
*/
public ArrayList<Double> getCountRightSlab() {
return countRightSlab;
}
}