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

218 lines
6.4 KiB
Java

package Presenter.Algorithms;
import Model.Line;
import Model.Point;
import Model.Slab;
import Presenter.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.Observable;
import java.util.concurrent.ThreadLocalRandom;
/**
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
*
* @Author: Armin Wolf
* @Email: a_wolf28@uni-muenster.de
* @Date: 28.05.2017.
*/
public class TheilSenEstimator extends Observable implements Algorithm {
private Presenter presenter;
private ArrayList<Line> setOfLines;
private ArrayList<Point> setOfIntersections;
private ArrayList<Point> intervalIntersections;
private ArrayList<Double> yCoordinates;
private ArrayList<Double> xCoordinates;
private Slab interval;
private Double j;
private Integer jA;
private Integer jB;
private Double r;
private Integer n;
private Double N;
private Integer k;
private Double a;
private Double b;
private Double aVariant;
private Double bVariant;
private ArrayList<Double> sampledIntersections;
public TheilSenEstimator(LinkedList<Line> setOfLines, LinkedList<Point> setOfIntersections, Presenter presenter) {
this.presenter = presenter;
this.setOfLines = new ArrayList<>(setOfLines);
this.setOfIntersections = new ArrayList<>(setOfIntersections);
this.intervalIntersections = new ArrayList<>(setOfIntersections);
this.n = setOfLines.size();
this.sampledIntersections = new ArrayList<>();
this.yCoordinates = new ArrayList<>();
this.xCoordinates = new ArrayList<>();
this.N = BinomialCoeffizient.run(n, 2);
this.k = Integer.valueOf((int) (N * 0.5));
for (Point l : setOfIntersections){
yCoordinates.add(l.getY());
xCoordinates.add(l.getX());
}
}
/**
*
*/
public void run(){
a = -90000d;
b = 90000d;
Collections.sort(setOfIntersections);
interval = new Slab(a,b);
while (true){
if (this.N <= n){
break;
} else {
r = Double.valueOf(n);
this.k = Integer.valueOf((int) (N * 0.5));
System.out.println("#number: "+N);
int numberOfIntersections = checkNumberOfIntersectionInInterval(-90000,a, setOfIntersections);
j = (r /N) * (k - numberOfIntersections);
jA = (int) Math.floor(j - (3 * Math.sqrt(r)));
jB = (int) Math.floor(j + (3 * Math.sqrt(r)));
do {
sampledIntersections = randomSampleOfIntersections(intervalIntersections, r);
Collections.sort(sampledIntersections);
aVariant = sampledIntersections.get(jA);
bVariant = sampledIntersections.get(jB);
} while (!checkCondition());
a = aVariant;
b = bVariant;
interval.setLower(a);
interval.setUpper(b);
N = Double.valueOf(checkNumberOfIntersectionInInterval(a,b, intervalIntersections));
intervalIntersections = getKleftMostIntersection(a,b);
}
}
}
/**
* Diese Funktion überprüft ob die Bedingung für das Interval erfüllt ist. Dabei muss der k-te
* Schnittpunkt in diesem Interval enthalten sein. des weiteren soll die Anzahl der Schnittpunkte
* im Interval kleiner oder gleich dem Term: (11*N)/sqrt(r) sein.
*
* @return Boolscher Wert ob die Bedingung erfüllt ist
*/
private Boolean checkCondition(){
Collections.sort(intervalIntersections);
Boolean cond1 = (intervalIntersections.get(k-1).getX() >= aVariant) && (
intervalIntersections.get(k-1).getX() < bVariant);
Boolean cond2 = (checkNumberOfIntersectionInInterval(aVariant,bVariant, intervalIntersections) <= ((11 * N) / Math.sqrt(r)));
return cond1 && cond2;
}
/**
* Diese Funktion gibt eine <code>r</code> Elementige Stichprobe aus der überegebenene Menge an
* Schnittpunkten. Diese Stichprobe soll zufällig sein. Es können aus gleiche Werte in der Rückgabe
* vertreten sein.
*
* @param set Menge an Schnittpunkten
* @param r Stichprobengröße
* @return Stichprobe
*/
public ArrayList<Double> randomSampleOfIntersections(ArrayList<Point> set, Double r){
ArrayList<Double> sampledLines = new ArrayList<>();
while (sampledLines.size() < r){
Double x = set.get(ThreadLocalRandom.current().nextInt(0, set.size())).getX();
if (!sampledLines.contains(x))
sampledLines.add(x);
}
return sampledLines;
}
/**
* Berechne wieviele von den Schnittpunkten in dem Interval zwischen <code>a</code> und <code>b</code>
* enthalten sind.
*
* @param a untere Grenze des Intervals
* @param b obere Grenze des Intrvals
* @return Anzahl der Schnittpunkte im Interval [a,b)
*/
public int checkNumberOfIntersectionInInterval(double a, double b, ArrayList<Point> set){
int counter = 0;
for (Point x : set){
if (x.getX() >= a && x.getX() < b){
counter++;
}
}
return counter;
}
/**
* Berechne wieviele von den Schnittpunkten in dem Interval zwischen <code>a</code> und <code>b</code>
* enthalten sind. Zusätzlich werden diese Schnittpunkte in einer Liste festgehalten und diese werden
* zurückgeliefert.
*
* @param a untere Grenze des Intervals
* @param b obere Grenze des Intrvals
* @return Liste der Schnittpunkte die im Interval [a,b) vertreten sind
*/
public ArrayList<Point> getKleftMostIntersection(double a, double b){
ArrayList<Point> list = new ArrayList<>();
for (Point x : intervalIntersections){
if (x.getX() >= a && x.getX() < b){
list.add(x);
}
}
return list;
}
@Override
public void getResult() {
if (presenter != null) {
setChanged();
double m,x;
double b,y;
Collections.sort(xCoordinates);
Collections.sort(yCoordinates);
int n = xCoordinates.size();
if (n % 2 == 0){
x = 0.5 * (xCoordinates.get((n/2)-1) + xCoordinates.get((n/2)));
y = 0.5 * (yCoordinates.get((n/2)-1) + yCoordinates.get((n/2)));
} else {
x = xCoordinates.get(((n+1)/2)-1);
y = yCoordinates.get(((n+1)/2)-1);
}
ArrayList<Point> resultSt = getKleftMostIntersection(a, this.b);
int size = resultSt.size();
if (size % 2 == 0){
m = 0.5 * (resultSt.get((size/2)-1).getX() + resultSt.get((size/2)).getX());
} else {
m = resultSt.get(((size+1)/2)-1).getX();
}
m *= -1;
b = (x * m) + y;
String[] result = new String[]{"ts", m+"", b+""};
notifyObservers(result);
}
}
}