algorithms-for-computing-li.../src/main/java/Presenter/Algorithms/LeastMedianOfSquaresEstimat...

336 lines
9.6 KiB
Java

package Presenter.Algorithms;
import Model.Coordinates;
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<Coordinates> set = new LinkedList<>();
private LinkedList<Coordinates> intersections = new LinkedList<>();
private int n;
private final double quantile = 0.5;
private final double error = 0.01;
private double quantileError;
private double qPlus;
private double qMinus;
private double kPlus;
private double kMinus;
private Set<Slab> slab;
private Slab activeSlab;
private Slab subSlabU1;
private Slab subSlabU2;
private ArrayList<Double> sortedLineSequence = new ArrayList<>();
private double heightsigmaMin;
private Coordinates sigmaMinStart;
private Coordinates sigmaMinEnd;
private int numberOfIntersections;
private final int constant = 1;
private Coordinates kMinusBracelet;
private double intersectionsPoint;
/**
* 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;
}
}
public void approximateLMS() {
//(1.) Let n <- |S|; q+ <- q; q- <- q+ * (1 - quantileError);....
n = set.size();
qPlus = quantile;
qMinus = qPlus * (1 - quantileError);
kMinus = Math.ceil(n * qMinus);
kPlus = Math.ceil(n * qPlus);
//(2.) Let U <- (-inf, inf) be the initial active slab...
slab = new TreeSet<>();
slab.add(new Slab(Double.MAX_VALUE, Double.MIN_VALUE));
heightsigmaMin = Double.MAX_VALUE;
//(3.) Apply the following steps as long as the exists active slabs
for (Iterator<Slab> it = slab.iterator(); it.hasNext(); ) {
//(a.) Select any active Slab and calc. the inversions
activeSlab = it.next();
numberOfIntersections = countInversions(activeSlab);
//(b.) apply plane sweep
if (numberOfIntersections < (constant * n)) {
kMinusBracelet = planeSweep(activeSlab);
} else {//(c.) otherwise....
//get random intersections point...
splitActiveSlab(intersectionsPoint);
}
//(d.) this may update sigma min
upperBound(intersectionsPoint);
//(e.) for i={1,2}, call lower bound(Ui)
lowerBound(subSlabU1);
lowerBound(subSlabU2);
}
}
//Parameter anpassen
/**
* @param slab
* @return
*/
public int countInversions(Slab slab) {
int numberOfInversions = 0;
ArrayList<Double> umin = new ArrayList<>();
ArrayList<Double> umax = new ArrayList<>();
for (Coordinates p : set) {
umin.add((slab.getLower() * p.getX()) + p.getY());
umax.add((slab.getUpper() * p.getX()) + p.getY());
}
numberOfInversions = mergeSort(umin, 0, umin.size() - 1, umax);
for (Coordinates point : intersections) {
if (point.getX() >= slab.getLower() && point.getX() < slab.getUpper()) {
intersectionsPoint = point.getX();
break;
}
}
return numberOfInversions;
}
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 Coordinates planeSweep(Slab slab) {
Comparator<Coordinates> queueComparator = (o1, o2) -> {
if (o1.getX() == o2.getX()) {
if (o1.getY() <= o2.getY()) {
return -1;
} else {
return 1;
}
} else if (o1.getX() < o2.getX()) {
return -1;
} else {
return 1;
}
};
PriorityQueue<Coordinates> xQueue = new PriorityQueue<>(queueComparator);
Comparator<Coordinates> treeComparator = (o1, o2) -> {
if (o1.getY() == o2.getY()) {
if (o1.getX() <= o2.getX()) {
return -1;
} else {
return 1;
}
} else if (o1.getY() < o2.getY()) {
return -1;
} else {
return 1;
}
};
TreeMap<Double,Coordinates> yStruct = new TreeMap(treeComparator);
for (Coordinates point : intersections) {
if (point.getX() >= slab.getLower() && point.getX() < slab.getUpper()) {
xQueue.add(point);
}
}
return new Coordinates(.0, .0);
}
/**
* @param point
*/
public void splitActiveSlab(double point) {
subSlabU1 = new Slab(activeSlab.getLower(), point);
subSlabU2 = new Slab(point, activeSlab.getUpper());
}
/**
* @param point
*/
public void upperBound(double point) {
ArrayList<Double> min = new ArrayList<>();
double height;
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) {
sigmaMinStart = new Coordinates(point, sortedLineSequence.get(i + (((int) kMinus) - 1)));
sigmaMinEnd = new Coordinates(point, sortedLineSequence.get(i));
}
}
}
/**
* @param slab
* @return
*/
public Slab lowerBound(Slab slab) {
boolean active = false;
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<Coordinates> lines = new ArrayList<>();
for (Coordinates p : set) {
lines.add(new Coordinates(((slab.getLower() * p.getX()) + p.getY()), ((slab.getUpper() * p.getX()) + p.getY())));
}
umaxList = getEjValues(slab.getUpper());
uminList = getEjValues(slab.getLower());
for (int i = 1; i < n; i++) {
Coordinates level = new Coordinates(uminList.get(i), umaxList.get(i));
for (Coordinates 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;
active = false;
for (int j = 0; j < n; j++) {
do {
i++;
} while ((i < n) && (beta[i] - alpha[j] < kPlus));
if (i > n) {
slab.setActivity(false);
break;
}
h = Math.min((uminList.get(j) - uminList.get(i)), (umaxList.get(j) - umaxList.get(i)));
}
if (((1 + error) * h) < heightsigmaMin) {
slab.setActivity(true);
}
return 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 (Coordinates p : set) {
ret.add((p.getX() * u) + p.getY());
}
Collections.sort(ret);
return ret;
}
}