363 lines
10 KiB
Java
363 lines
10 KiB
Java
package Presenter.Algorithms;
|
|
|
|
import Model.Line;
|
|
import Model.Point;
|
|
|
|
import java.util.*;
|
|
|
|
/**
|
|
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
|
|
*
|
|
* @Author: Armin Wolf
|
|
* @Email: a_wolf28@uni-muenster.de
|
|
* @Date: 28.05.2017.
|
|
*/
|
|
public class LeastMedianOfSquaresEstimator extends Algorithm {
|
|
|
|
|
|
private LinkedList<Line> set = new LinkedList<>();
|
|
private LinkedList<Point> intersections = new LinkedList<>();
|
|
private int n;
|
|
private double quantileError;
|
|
private double kPlus;
|
|
private double kMinus;
|
|
private ArrayDeque<Slab> slabs;
|
|
private Slab subSlabU1;
|
|
private Slab subSlabU2;
|
|
private Line sigmaMin;
|
|
private double heightsigmaMin;
|
|
private double intersectionsPoint;
|
|
|
|
public LeastMedianOfSquaresEstimator(LinkedList<Line> set, LinkedList<Point> intersections) {
|
|
this.set = set;
|
|
this.intersections = intersections;
|
|
}
|
|
|
|
public void printResult(){
|
|
System.out.println("RESULT: "+sigmaMin.getM()+"x +"+sigmaMin.getB());
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public void approximateLMS() {
|
|
//(1.) Let n <- |S|; q+ <- q; q- <- q+ * (1 - quantileError);....
|
|
n = set.size();
|
|
double quantile = 0.5;
|
|
double qPlus = quantile;
|
|
double qMinus = qPlus * (1 - quantileError);
|
|
kMinus = Math.ceil(n * qMinus);
|
|
kPlus = Math.ceil(n * qPlus);
|
|
|
|
//(2.) Let U <- (-inf, inf) be the initial active slabs...
|
|
slabs = new ArrayDeque<>();
|
|
slabs.add(new Slab(-100000, 100000));
|
|
heightsigmaMin = Double.MAX_VALUE;
|
|
|
|
//(3.) Apply the following steps as long as the exists active slabs
|
|
while (!slabs.isEmpty()) {
|
|
Slab slab = slabs.getFirst();
|
|
//(a.) Select any active Slab and calc. the inversions
|
|
int numberOfIntersections = countInversions(slab);
|
|
|
|
//(b.) apply plane sweep
|
|
int constant = 1;
|
|
if (numberOfIntersections < (constant * n)) {
|
|
sigmaMin = planeSweep(slab);
|
|
} else {//(c.) otherwise....
|
|
//get random intersections point...
|
|
splitActiveSlab(intersectionsPoint, slab);
|
|
}
|
|
//(d.) this may update sigma min
|
|
upperBound(intersectionsPoint);
|
|
//(e.) for i={1,2}, call lower bound(Ui)
|
|
lowerBound(subSlabU1);
|
|
lowerBound(subSlabU2);
|
|
}
|
|
|
|
// printResult();
|
|
}
|
|
|
|
/**
|
|
* @param slab
|
|
* @return
|
|
*/
|
|
public int countInversions(Slab slab) {
|
|
|
|
int numberOfInversions = 0;
|
|
|
|
ArrayList<Double> umin = new ArrayList<>();
|
|
ArrayList<Double> umax = new ArrayList<>();
|
|
ArrayList<Double> randomIntersection = new ArrayList<>();
|
|
|
|
for (Line p : set) {
|
|
umin.add((slab.getLower() * p.getM()) + p.getB());
|
|
umax.add((slab.getUpper() * p.getM()) + p.getB());
|
|
}
|
|
|
|
numberOfInversions = mergeSort(umin, 0, umin.size() - 1, umax);
|
|
|
|
for (Point point : intersections) {
|
|
if (point.getX() >= slab.getLower() && point.getX() < slab.getUpper()) {
|
|
randomIntersection.add(point.getX());
|
|
}
|
|
}
|
|
|
|
Collections.shuffle(randomIntersection);
|
|
intersectionsPoint = randomIntersection.get(0);
|
|
|
|
return numberOfInversions;
|
|
}
|
|
|
|
//Parameter anpassen
|
|
|
|
/**
|
|
*
|
|
* @param a
|
|
* @param start
|
|
* @param end
|
|
* @param aux
|
|
* @return
|
|
*/
|
|
public int mergeSort(List<Double> a, int start, int end, List<Double> aux) {
|
|
if (start >= end) {
|
|
return 0;
|
|
}
|
|
int invCount = 0;
|
|
int mid = start + (end - start) / 2;
|
|
int invCountLeft = mergeSort(a, start, mid, aux); // divide and conquer
|
|
int invCountRight = mergeSort(a, mid + 1, end, aux); // divide and conquer
|
|
invCount += (invCountLeft + invCountRight);
|
|
for (int i = start; i <= end; i++) {
|
|
aux.set(i, a.get(i));
|
|
}
|
|
int left = start;
|
|
int right = mid + 1;
|
|
int index = start;
|
|
while (left <= mid && right <= end) {
|
|
if (aux.get(left) < aux.get(right)) {
|
|
a.set(index++, aux.get(left++));
|
|
} else {
|
|
a.set(index++, aux.get(right++));
|
|
invCount += mid - left + 1; // number of inversions for aux[right]
|
|
}
|
|
}
|
|
while (left <= mid) {
|
|
a.set(index++, aux.get(left++));
|
|
}
|
|
// no need to copy over remaining aux[right++] because they are already inside a
|
|
return invCount;
|
|
}
|
|
|
|
/**
|
|
* @param slab
|
|
* @return
|
|
*/
|
|
public Line planeSweep(Slab slab) {
|
|
|
|
//initialisiere die x-Queue mit den 2D Punkten und sortiere nach x-Lexikographischer Ordnung
|
|
ArrayList<Point> xQueue = new ArrayList<>();
|
|
for (Point point : intersections) {
|
|
if (point.getX() >= slab.getLower() && point.getX() < slab.getUpper()) {
|
|
xQueue.add(point);
|
|
}
|
|
}
|
|
Collections.sort(xQueue);
|
|
|
|
|
|
Line bracelet = sigmaMin;
|
|
double heightOfBracelet = heightsigmaMin;
|
|
|
|
for (Point current : xQueue){
|
|
double[] currentBracelet = calcKMinusBracelet(current);
|
|
|
|
if (currentBracelet == null){
|
|
continue;
|
|
} else if (currentBracelet[0] < heightOfBracelet){
|
|
heightOfBracelet = currentBracelet[0];
|
|
bracelet = new Line(current.getX(), current.getX(), currentBracelet[1], currentBracelet[2]);
|
|
System.out.println("R: "+bracelet.getM()+"x +"+bracelet.getB());
|
|
}
|
|
}
|
|
|
|
|
|
return bracelet;
|
|
}
|
|
|
|
/**
|
|
* @param point
|
|
*/
|
|
public void splitActiveSlab(double point, Slab active) {
|
|
|
|
subSlabU1 = new Slab(active.getLower(), point);
|
|
subSlabU2 = new Slab(point, active.getUpper());
|
|
this.slabs.removeFirst();
|
|
}
|
|
|
|
/**
|
|
* @param point
|
|
*/
|
|
public void upperBound(double point) {
|
|
|
|
double height;
|
|
|
|
ArrayList<Double> sortedLineSequence = getEjValues(point);
|
|
|
|
for (int i = 1; i < (n - (kMinus + 1)); i++) {
|
|
height = sortedLineSequence.get(i + (((int) kMinus) - 1)) - sortedLineSequence.get(i);
|
|
|
|
if (height < heightsigmaMin) {
|
|
sigmaMin.setEndPoints(point, sortedLineSequence.get(i + (((int) kMinus) - 1))
|
|
,point, sortedLineSequence.get(i));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @param slab
|
|
* @return
|
|
*/
|
|
public void lowerBound(Slab slab) {
|
|
|
|
int[] alpha = new int[n];
|
|
int[] beta = new int[n];
|
|
alpha[0] = 0;
|
|
beta[0] = 0;
|
|
int strictlyGreater = 0;
|
|
|
|
//Teil I.
|
|
ArrayList<Double> umaxList;
|
|
ArrayList<Double> uminList;
|
|
|
|
//y koordinaten der Schnittpunkte
|
|
ArrayList<Point> lines = new ArrayList<>();
|
|
System.out.println("Anzahl der Slabs: "+this.slabs.size());
|
|
for (Line p : set) {
|
|
lines.add(new Point(((slab.getLower() * p.getM()) + p.getB()), ((slab.getUpper() * p.getM()) + p.getB())));
|
|
}
|
|
|
|
|
|
umaxList = getEjValues(slab.getUpper());
|
|
uminList = getEjValues(slab.getLower());
|
|
|
|
for (int i = 1; i < n; i++) {
|
|
Point level = new Point(uminList.get(i), umaxList.get(i));
|
|
for (Point point : lines) {
|
|
if ((point.getX() < level.getX()) && (point.getY() < level.getY())) {
|
|
alpha[i]++;
|
|
}
|
|
|
|
if ((point.getX() > level.getX()) && (point.getY() > level.getY())) {
|
|
strictlyGreater++;
|
|
}
|
|
}
|
|
beta[i] = n - (alpha[i] + strictlyGreater);
|
|
}
|
|
|
|
//Teil II.
|
|
int i = 1;
|
|
double h = Double.MAX_VALUE;
|
|
for (int j = 1; j < n; j++) {
|
|
while (((i < n) && (Math.abs(beta[i] - alpha[j]) < kPlus))){
|
|
System.out.println("i: "+i+"\t "+Math.abs(beta[i] - alpha[j])+"\t kPlus: "+kPlus);
|
|
i++;
|
|
}
|
|
|
|
if (i >= n) {
|
|
break;
|
|
} else {
|
|
h = Math.min((uminList.get(j) - uminList.get(i)), (umaxList.get(j) - umaxList.get(i)));
|
|
}
|
|
}
|
|
double error = 0.01;
|
|
System.out.println("h: "+h);
|
|
if (((1 + error) * h) < heightsigmaMin) {
|
|
this.slabs.addLast(slab);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Berechnet die Schnittpunkte der Geraden und der vertikalen Gerade u. Im paper sind diese Werte als e_j Werte
|
|
* bekannt.
|
|
*
|
|
* @param u vertikale Gerade
|
|
* @return Liste der Schnittpunkte (da u bekannt werden nur die y Werte zurück gegeben)
|
|
*/
|
|
public ArrayList<Double> getEjValues(double u) {
|
|
|
|
ArrayList<Double> ret = new ArrayList<>();
|
|
|
|
for (Line p : set) {
|
|
ret.add((p.getM() * u) + p.getB());
|
|
}
|
|
|
|
Collections.sort(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param x
|
|
* @return
|
|
*/
|
|
public double[] calcKMinusBracelet(Point x) {
|
|
|
|
//y Koordinaten für das kMinus brecalet
|
|
LinkedList<Double> intersections = new LinkedList<>();
|
|
for (Line line : set) {
|
|
intersections.add((x.getX() * line.getM())+line.getB());
|
|
}
|
|
if (intersections.size() < kMinus){
|
|
return null;
|
|
} else {
|
|
Collections.sort(intersections);
|
|
double height = Math.abs(intersections.getFirst() - intersections.getLast());
|
|
double[] ret = {height, intersections.getFirst(), intersections.getLast()};
|
|
return ret;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Hilfsklasse um die Slabs zu verteilen, private Klasse da sonst nicht verwendett wird und somit eine
|
|
* äußere Klasse überflüssig ist...
|
|
*/
|
|
private static class Slab {
|
|
private double upper;
|
|
private double lower;
|
|
private Boolean activity;
|
|
|
|
public Slab(double lower, double upper) {
|
|
this.upper = upper;
|
|
this.lower = lower;
|
|
}
|
|
|
|
public Boolean getActivity() {
|
|
return activity;
|
|
}
|
|
|
|
public void setActivity(Boolean isActive) {
|
|
this.activity = isActive;
|
|
}
|
|
|
|
public double getUpper() {
|
|
return upper;
|
|
}
|
|
|
|
public void setUpper(double upper) {
|
|
this.upper = upper;
|
|
}
|
|
|
|
public double getLower() {
|
|
return lower;
|
|
}
|
|
|
|
public void setLower(double lower) {
|
|
this.lower = lower;
|
|
}
|
|
|
|
}
|
|
}
|