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

481 lines
14 KiB
Java

package presenter.algorithms.advanced;
import model.Interval;
import model.Line;
import model.Point;
import presenter.Presenter;
import presenter.algorithms.Algorithm;
import presenter.algorithms.util.IntersectionCounter;
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 Observable implements Algorithm {
private Presenter presenter;
private LinkedList<Line> set = new LinkedList<>();
private LinkedList<Point> intersections = new LinkedList<>();
private IntersectionCounter invCounter = new IntersectionCounter();
private int n;
private double quantileError;
private int kPlus;
private int kMinus;
private PriorityQueue<Interval> intervals;
private Interval subSlabU1;
private Interval subSlabU2;
private Line sigmaMin;
private double heightsigmaMin;
private Double intersectionsPoint;
private Double constant = 0.5;
private Double slope;
private Double yInterception;
public LeastMedianOfSquaresEstimator(LinkedList<Line> set, LinkedList<Point> intersections,
Presenter presenter) {
this.set = set;
this.intersections = intersections;
//(1.) Let n <- |S|; q+ <- q; q- <- q+ * (1 - quantileError);....
n = set.size();
double quantile = 0.5;
double qPlus = quantile;
quantileError = 0.1;
double qMinus = qPlus * (1 - quantileError);
kMinus = (int) Math.ceil(n * qMinus);
kPlus = (int) Math.ceil(n * qPlus);
this.presenter = presenter;
}
public LeastMedianOfSquaresEstimator(LinkedList<Line> set, LinkedList<Point> intersections) {
this(set, intersections, null);
}
/**
*
*/
public void run() {
//(2.) Let U <- (-inf, inf) be the initial active intervals...
Comparator<Interval> comparator = (o1, o2) -> {
if (o1.getDistance() < o2.getDistance()) {
return -1;
}
if (o1.getDistance() > o2.getDistance()) {
return 1;
} else {
return 0;
}
};
intervals = new PriorityQueue<>(comparator);
intervals.add(new Interval(-100000, 100000));
heightsigmaMin = Double.MAX_VALUE;
LinkedList<Point> tmpIntersections = intersections;
//(3.) Apply the following steps as long as the exists active intervals
boolean active = true;
Interval interval;
while (!this.intervals.isEmpty()) {
interval = this.intervals.peek();
if (interval.getActivity()) {
//(a.) Select any active Interval and calc. the inversions
int numberOfIntersections = countInversions(interval);
//(b.) apply plane sweep
if ((constant * n) >= numberOfIntersections) {
sigmaMin = planeSweep(interval);
} else {
//(c.) otherwise....
// get random intersections point...
Collections.shuffle(tmpIntersections, new Random());
for (int i = 0; i < tmpIntersections.size(); i++) {
if (tmpIntersections.get(i).getX() > interval.getLower()
&& tmpIntersections.get(i).getX() < interval.getUpper()) {
intersectionsPoint = tmpIntersections.get(i).getX();
break;
} else {
intersectionsPoint = null;
}
}
if (intersectionsPoint != null) {
splitActiveSlab(intersectionsPoint, interval);
//(d.) this may update sigma min
upperBound(intersectionsPoint);
//(e.) for i={1,2}, call lower bound(Ui)
lowerBound(subSlabU1);
lowerBound(subSlabU2);
if (subSlabU1.getActivity()) {
this.intervals.add(subSlabU1);
}
if (subSlabU2.getActivity()) {
this.intervals.add(subSlabU2);
}
} else {
this.intervals.poll();
}
}
} else {
this.intervals.remove(interval);
}
}
}
/**
* @param interval
* @return
*/
public int countInversions(Interval interval) {
int numberOfInversions = 0;
// debug
//for (int i=0;i<listA.size();i++){
// System.out.println(listA.get(i)+", "+listB.get(i));
//}
numberOfInversions = invCounter.run(set, interval);
return numberOfInversions;
}
/**
* @param interval
* @return
*/
public Line planeSweep(Interval interval) {
//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() >= interval.getLower() && point.getX() < interval.getUpper()) {
xQueue.add(point);
}
}
Collections.sort(xQueue);
Line bracelet = sigmaMin;
double heightOfBracelet = heightsigmaMin;
for (Point current : xQueue) {
Double[] currentBracelet = calcKMinusBracelet(current, kMinus);
if (currentBracelet == null) {
continue;
} else if (currentBracelet[0] < heightOfBracelet) {
heightOfBracelet = currentBracelet[0];
bracelet = new Line(current.getX(), current.getX(), currentBracelet[1], currentBracelet[2]);
}
}
interval.setActivity(false);
return bracelet;
}
/**
* Diese Methode spaltet den aktiven Interval an der x Koordinate point. Es werden zwei neue Slabs
* erzeugt.
*
* @param point x Koordinate an der, der Split geschieht.
*/
public void splitActiveSlab(double point, Interval active) {
subSlabU1 = new Interval(active.getLower() + 0.01, point);
subSlabU2 = new Interval(point, active.getUpper());
this.intervals.remove(active);
}
/**
* @param point
*/
public void upperBound(double point) {
double height = heightsigmaMin;
double tmpHeight;
ArrayList<Double> sortedLineSequence = getEjValues(point);
int itnbr = ((n - kMinus) + 1);
for (int i = 0; i < itnbr; i++) {
tmpHeight = sortedLineSequence.get((i + kMinus) - 1) - sortedLineSequence.get(i);
if (tmpHeight < height) {
height = tmpHeight;
}
if (height < heightsigmaMin) {
heightsigmaMin = height;
if (sigmaMin != null) {
sigmaMin.setEndPoints(point, sortedLineSequence.get(i)
, point, sortedLineSequence.get((i + kMinus) - 1));
} else {
sigmaMin = new Line(point, point, sortedLineSequence.get(i),
sortedLineSequence.get((i + kMinus) - 1));
}
}
}
}
/**
* @param pslab
* @return
*/
public void lowerBound(Interval pslab) {
int[] alpha = new int[n];
int[] beta = new int[n];
int strictlyGreater = 0;
//Teil I.
ArrayList<Double> umaxList;
ArrayList<Double> uminList;
//y koordinaten der Schnittpunkte
ArrayList<Line> lines = new ArrayList<>();
for (Line p : set) {
lines.add(
new Line(pslab.getLower(), pslab.getUpper(), ((pslab.getLower() * p.getM()) + p.getB()),
((pslab.getUpper() * p.getM()) + p.getB())));
}
umaxList = getEjValues(pslab.getUpper());
uminList = getEjValues(pslab.getLower());
for (int i = 0; i < n; i++) {
Line level = new Line(pslab.getLower(), pslab.getUpper(), uminList.get(i), umaxList.get(i));
for (Line line : lines) {
if ((line.getY1() < level.getY1()) && (line.getY2() < level.getY2())) {
alpha[i]++;
}
if ((line.getY1() > level.getY1()) && (line.getY2() > level.getY2())) {
strictlyGreater++;
}
}
beta[i] = n - (alpha[i] + strictlyGreater);
strictlyGreater = 0;
}
//TEST der Alpha und Beta werte, siehe JUnit Test
//for (int i=0;i<alpha.length;i++){
// System.out.println("Alpha["+i+"]: "+alpha[i]+"\t Beta["+i+"]: "+beta[i]);
//}
//Test
//Teil II.
int i = 0;
double h;
pslab.setActivity(false);
for (int j = 0; j < n; j++) {
while ((i < n && (Math.abs(beta[i] - alpha[j]) < kPlus))) {
i++;
}
//test
//if (i < n)
// System.out.println("i: "+i+", j:"+j+"\t "+Math.abs(beta[i] - alpha[j])+"\t kPlus: "+kPlus);
if (i >= n) {
//System.out.println("i: "+i+", j:"+j+". ungültig");
pslab.setActivity(false);
break;
} else {
h = Math.min(Math.abs(uminList.get(j) - uminList.get(i)),
Math.abs(umaxList.get(j) - umaxList.get(i)));
double error = 0.01;
if (((1 + error) * h) < heightsigmaMin) {
//System.out.println("h: "+ h +" ist kleiner als height(sigmaMin): "+heightsigmaMin);
pslab.setActivity(true);
return;
}
}
i = 0;
}
}
/**
* 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;
}
/**
* Die Funktion berechnet anhand einer vertikalen Gerade x = px das sogenannte kleinste kMinus
* Bracelet. Mit anderen Worten es wird eine vertikale Teilgerade berechnet die mindestens kMinus
* Geraden schneidet und dabei minimal ist.
*
* @param px Koordinate um die "vertikale Gerade" zu simulieren.
* @return Das Array enthält höhe des Bracelet, e_j und e_(j + kMinus - 1)
*/
public Double[] calcKMinusBracelet(Point px, int kMinusValue) {
//y Koordinaten für das kMinus brecalet
LinkedList<Double> intersections = new LinkedList<>();
for (Line line : set) {
intersections.add((px.getX() * line.getM()) + line.getB());
}
if (intersections.size() >= kMinusValue) {
Collections.sort(intersections);
double height = Math.abs(intersections.get(0) - intersections.get(0 + kMinusValue - 1));
Double[] ret = {height, intersections.get(0), intersections.get(0 + kMinusValue - 1)};
return ret;
} else {
return null;
}
}
@Override
public void getResult() {
if (presenter != null) {
setChanged();
double m = (getSigmaMin().getX2() + getSigmaMin().getX1()) * 0.5;
double b = (getSigmaMin().getY2() + getSigmaMin().getY1()) * -0.5;
slope = m;
yInterception = b;
String[] result = {"lms", m + "", b + ""};
notifyObservers(result);
} else {
double m = (getSigmaMin().getX2() + getSigmaMin().getX1()) * 0.5;
double b = (getSigmaMin().getY2() + getSigmaMin().getY1()) * -0.5;
slope = m;
yInterception = b;
}
}
/**
* Im Allgemeinen werden keine Getter und Setter Methoden benötigt aber sie sind nützlich bei den
* JUnit Testfällen.
*/
public LinkedList<Line> getSet() {
return set;
}
public void setSet(LinkedList<Line> set) {
this.set = set;
}
public LinkedList<Point> getIntersections() {
return intersections;
}
public void setIntersections(LinkedList<Point> intersections) {
this.intersections = intersections;
}
public int getN() {
return n;
}
public void setN(int n) {
this.n = n;
}
public double getQuantileError() {
return quantileError;
}
public void setQuantileError(double quantileError) {
this.quantileError = quantileError;
}
public int getkPlus() {
return kPlus;
}
public void setkPlus(int kPlus) {
this.kPlus = kPlus;
}
public int getkMinus() {
return kMinus;
}
public void setkMinus(int kMinus) {
this.kMinus = kMinus;
}
public Interval getSubSlabU1() {
return subSlabU1;
}
public void setSubSlabU1(Interval subSlabU1) {
this.subSlabU1 = subSlabU1;
}
public Interval getSubSlabU2() {
return subSlabU2;
}
public void setSubSlabU2(Interval subSlabU2) {
this.subSlabU2 = subSlabU2;
}
public Line getSigmaMin() {
return sigmaMin;
}
public void setSigmaMin(Line sigmaMin) {
this.sigmaMin = sigmaMin;
}
public double getHeightsigmaMin() {
return heightsigmaMin;
}
public void setHeightsigmaMin(double heightsigmaMin) {
this.heightsigmaMin = heightsigmaMin;
}
public double getIntersectionsPoint() {
return intersectionsPoint;
}
public void setIntersectionsPoint(double intersectionsPoint) {
this.intersectionsPoint = intersectionsPoint;
}
public Double getConstant() {
return constant;
}
public void setConstant(Double constant) {
this.constant = constant;
}
public Double getSlope() {
return slope;
}
public Double getyInterception() {
return yInterception;
}
}