added Bentley-Ottman Plane Sweep algorithm for intersection computation. Adjusted the algorithms.

This commit is contained in:
Armin Wolf 2020-05-02 15:07:02 +02:00
parent 91d3fc4c97
commit 14d146a452
17 changed files with 481 additions and 596 deletions

View File

@ -0,0 +1,64 @@
package de.wwwu.awolf.model.dao;
import de.wwwu.awolf.model.dao.Line.Segment;
import de.wwwu.awolf.model.dao.Point;
import java.util.*;
/**
* Created by valen_000 on 14. 5. 2017.
*/
public class Event {
private Point point;
private ArrayList<Segment> segments;
private double value;
private int type;
public Event(Point p, Segment s, int type) {
this.point = p;
this.segments = new ArrayList<>(Arrays.asList(s));
this.value = p.getX();
this.type = type;
}
public Event(Point p, ArrayList<Segment> s, int type) {
this.point = p;
this.segments = s;
this.value = p.getX();
this.type = type;
}
public void add_point(Point p) {
this.point = p;
}
public Point get_point() {
return this.point;
}
public void add_segment(Segment s) {
this.segments.add(s);
}
public ArrayList<Segment> get_segments() {
return this.segments;
}
public void set_type(int type) {
this.type = type;
}
public int get_type() {
return this.type;
}
public void set_value(double value) {
this.value = value;
}
public double get_value() {
return this.value;
}
}

View File

@ -13,52 +13,18 @@ import org.apache.commons.math3.util.Precision;
public class Line implements Comparable<Line> {
private static final double EPSILON = 0.00001;
private static final double MIN = 9999d;
private static final double MAX = -9999d;
private double x1;
private double x2;
private double y1;
private double y2;
private Segment segment;
private Double m;
private Double b;
private Double x1;
private Double x2;
private Double y1;
private Double y2;
private String id;
/**
* Konstruktor
*
* @param m Steigung
* @param b y-Achsenabschnitt
* @param id id
*/
public Line(double m, double b, String id) {
public Line(Double m, Double b) {
this.m = m;
this.b = b;
this.x1 = MAX;
this.y1 = (MAX * m) + b;
this.x2 = MIN * 0.5;
this.y2 = ((MIN * 0.5) * m) + b;
this.id = id;
}
/**
* Konstruktor
*
* @param m Steigung
* @param b y-Achsenabschnitt
*/
public Line(double m, double b) {
this.m = m;
this.b = b;
this.x1 = calculateX1(MAX);
this.y1 = calculateY1(MAX);
this.x2 = calculateX2(MIN * 0.5);
this.y2 = calculateY2(MIN * 0.5);
}
/**
@ -71,29 +37,12 @@ public class Line implements Comparable<Line> {
*/
public Line(double x1, double x2, double y1, double y2) {
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
this.x2 = x2;
this.m = (y2 - y1) / (x2 - x1);
this.b = y2 - (x2 * m);
}
public Double calculateX1(Double min) {
return min;
}
public Double calculateY1(Double min) {
return (min * (m * -1)) + b;
}
public Double calculateX2(Double max) {
return max;
}
public Double calculateY2(Double max) {
return (max * (m * -1)) + b;
}
/**
@ -138,51 +87,6 @@ public class Line implements Comparable<Line> {
this.id = id;
}
/**
* @return x-Koordiante des Startpunkts
*/
public Double getX1() {
return x1;
}
/**
* @return x-Koordiante des Endpunkts
*/
public Double getX2() {
return x2;
}
/**
* @return y-Koordiante des Startpunkts
*/
public Double getY1() {
return y1;
}
/**
* @return y-Koordiante des Endpunkts
*/
public Double getY2() {
return y2;
}
/**
* Setzt die Koordianten des Segments. Aus dem Segment wird eine Gerade berechnet.
*
* @param x1 x-Koordiante des Startpunkts
* @param y1 y-Koordiante des Endpunkts
* @param x2 x-Koordinate des Startpunkts
* @param y2 y-Koordinte des Endpunkts
*/
public void setEndPoints(double x1, double y1, double x2, double y2) {
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
this.m = (y2 - y1) / (x2 - x1);
this.b = y2 - (x2 * m);
}
/**
* Vergleich einzelner Geradern
*
@ -218,74 +122,6 @@ public class Line implements Comparable<Line> {
return new Point(x, y);
}
// Given three colinear points p, q, r, the function checks if
// point q lies on line segment 'pr'
public boolean onSegment(Point p, Point q, Point r) {
return q.getX() <= Math.max(p.getX(), r.getX()) && q.getX() >= Math
.min(p.getX(), r.getX()) &&
q.getY() <= Math.max(p.getY(), r.getY()) && q.getY() >= Math
.min(p.getY(), r.getY());
}
// To find orientation of ordered triplet (p, q, r).
// The function returns following values
// 0 --> p, q and r are colinear
// 1 --> Clockwise
// 2 --> Counterclockwise
public int orientation(Point p, Point q, Point r) {
// See https://www.geeksforgeeks.org/orientation-3-ordered-points/
// for details of below formula.
double val = (q.getY() - p.getY()) * (r.getX() - q.getX()) -
(q.getX() - p.getX()) * (r.getY() - q.getY());
if (val == 0) {
return 0; // colinear
}
return (val > 0) ? 1 : 2; // clock or counterclock wise
}
// The main function that returns true if line segment 'p1q1'
// and 'p2q2' intersect.
// Line A: y = mx + b -->
public boolean doIntersect(Line line, double lower, double upper) {
//this
Point p1 = new Point(calculateX1(lower), calculateY1(lower));
Point q1 = new Point(calculateX2(upper), calculateY2(upper));
Point p2 = new Point(line.calculateX1(lower), line.calculateY1(lower));
Point q2 = new Point(line.calculateX2(upper), line.calculateY2(upper));
// Find the four orientations needed for general and
// special cases
int o1 = orientation(p1, q1, p2);
int o2 = orientation(p1, q1, q2);
int o3 = orientation(p2, q2, p1);
int o4 = orientation(p2, q2, q1);
// General case
if (o1 != o2 && o3 != o4) {
return true;
}
// Special Cases
// p1, q1 and p2 are colinear and p2 lies on segment p1q1
if (o1 == 0 && onSegment(p1, p2, q1)) {
return true;
}
// p1, q1 and q2 are colinear and q2 lies on segment p1q1
if (o2 == 0 && onSegment(p1, q2, q1)) {
return true;
}
// p2, q2 and p1 are colinear and p1 lies on segment p2q2
if (o3 == 0 && onSegment(p2, p1, q2)) {
return true;
}
// p2, q2 and q1 are colinear and q1 lies on segment p2q2
return o4 == 0 && onSegment(p2, q1, q2);// Doesn't fall in any of the above cases
}
@Override
public int compareTo(Line line) {
if (Precision.compareTo(this.getM(), line.getM(), EPSILON) == 0) {
@ -294,4 +130,89 @@ public class Line implements Comparable<Line> {
return this.getM().compareTo(line.getM());
}
}
public Segment getSegment(Interval interval) {
double xLow = interval.getLower();
double yLow = Double.NEGATIVE_INFINITY;
double xHigh = interval.getUpper();
double yHigh = Double.POSITIVE_INFINITY;
if (interval.getLower() > Double.MIN_VALUE && !Double.isNaN((interval.getLower() * m + b)) && !Double.isInfinite(interval.getLower() * m + b)) {
yLow = interval.getLower() * m + b;
}
if (interval.getUpper() < Double.MAX_VALUE && !Double.isNaN(interval.getUpper() * m + b) && !Double.isInfinite(interval.getUpper() * m + b) ) {
yHigh = interval.getUpper() * m + b;
}
this.segment = new Segment(new Point(xLow, yLow), new Point(xHigh, yHigh));
return this.segment;
}
public Segment getSegment() {
this.segment = new Segment(new Point(x1, y1), new Point(x2, y2));
return this.segment;
}
public double getX1() {
return x1;
}
public double getX2() {
return x2;
}
public double getY1() {
return y1;
}
public double getY2() {
return y2;
}
public static class Segment {
private Point p_1;
private Point p_2;
double value;
Segment(Point p_1, Point p_2) {
this.p_1 = p_1;
this.p_2 = p_2;
this.calculate_value(this.first().getX());
}
public Point first() {
if(p_1.getX() <= p_2.getX()) {
return p_1;
} else {
return p_2;
}
}
public Point second() {
if(p_1.getX() <= p_2.getX()) {
return p_2;
} else {
return p_1;
}
}
public void calculate_value(double value) {
double x1 = this.first().getX();
double x2 = this.second().getX();
double y1 = this.first().getY();
double y2 = this.second().getY();
this.value = y1 + (((y2 - y1) / (x2 - x1)) * (value - x1));
}
public void set_value(double value) {
this.value = value;
}
public double get_value() {
return this.value;
}
}
}

View File

@ -16,10 +16,6 @@ public class LineModel {
private Double min;
private Set<Line> lines;
private Double xMinimum;
private Double xMaximum;
private Double yMinimum;
private Double yMaximum;
private int size;
/**
@ -29,11 +25,6 @@ public class LineModel {
lines = new HashSet<>();
size = 0;
xMinimum = Double.MAX_VALUE;
xMaximum = Double.MIN_VALUE;
yMinimum = Double.MAX_VALUE;
yMaximum = Double.MIN_VALUE;
min = 0d;
max = 0d;
}
@ -73,72 +64,6 @@ public class LineModel {
this.size = lines.size();
}
/**
* @return Minimale x-Koordiante
*/
public Double getxMinimum() {
return xMinimum;
}
/**
* @param xMinimum Minimale x-Koordiante
*/
public void setxMinimum(Double xMinimum) {
this.xMinimum = xMinimum;
}
/**
* @return Maximale x-Koordiante
*/
public Double getxMaximum() {
return xMaximum;
}
/**
* @param xMaximum Maximale x-Koordiante
*/
public void setxMaximum(Double xMaximum) {
this.xMaximum = xMaximum;
}
/**
* @return Minimale y-Koordiante
*/
public Double getyMinimum() {
return yMinimum;
}
/**
* @param yMinimum Minimale y-Koordiante
*/
public void setyMinimum(Double yMinimum) {
this.yMinimum = yMinimum;
}
/**
* @return Maximale y-Koordiante
*/
public Double getyMaximum() {
return yMaximum;
}
/**
* @param yMaximum Maximale y-Koordiante
*/
public void setyMaximum(Double yMaximum) {
this.yMaximum = yMaximum;
}
/**
* Setzt die minimalen, maximalen x- und y-Koordinaten
*/
public void resetRanges() {
xMinimum = Double.MAX_VALUE;
xMaximum = Double.MIN_VALUE;
yMinimum = Double.MAX_VALUE;
yMaximum = Double.MIN_VALUE;
}
public int getSize() {
return size;
}

View File

@ -1,11 +1,12 @@
package de.wwwu.awolf.presenter.algorithms.advanced;
import de.wwwu.awolf.model.dao.Interval;
import de.wwwu.awolf.model.dao.Line;
import de.wwwu.awolf.model.dao.Point;
import de.wwwu.awolf.model.communication.AlgorithmMessage;
import de.wwwu.awolf.model.communication.Message;
import de.wwwu.awolf.model.communication.SubscriberType;
import de.wwwu.awolf.model.dao.Interval;
import de.wwwu.awolf.model.dao.Line;
import de.wwwu.awolf.model.dao.Line.Segment;
import de.wwwu.awolf.model.dao.Point;
import de.wwwu.awolf.presenter.AbstractPresenter;
import de.wwwu.awolf.presenter.algorithms.Algorithm;
import de.wwwu.awolf.presenter.util.IntersectionComputer;
@ -49,6 +50,7 @@ public class LeastMedianOfSquaresEstimator implements Algorithm {
private double yInterception;
private Flow.Subscriber<? super AlgorithmMessage> subscriber;
private Map<String, String> parameter;
private Set<Point> intersections;
public LeastMedianOfSquaresEstimator() {
parameter = new HashMap<>();
@ -78,27 +80,21 @@ public class LeastMedianOfSquaresEstimator implements Algorithm {
start = System.currentTimeMillis();
//(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;
}
};
Comparator<Interval> comparator = Comparator.comparing(Interval::getDistance);
intervals = new PriorityQueue<>(comparator);
intervals.add(new Interval(-10000, 10000));
intervals.add(new Interval(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
heightsigmaMin = Double.MAX_VALUE;
intersections = IntersectionComputer.getInstance().compute(setOfLines);
//(3.) Apply the following steps as long as the exists active intervals
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);
long numberOfIntersections = countIntersections(interval);
//(b.) apply plane sweep
if ((Double.parseDouble(parameter.get("Constant")) * n) >= numberOfIntersections) {
@ -106,35 +102,27 @@ public class LeastMedianOfSquaresEstimator implements Algorithm {
} else {
//(c.) otherwise....
// get random intersections point...
Collection<Point> tmpIntersections = IntersectionComputer
.getInstance()
.compute(setOfLines, interval.getLower(), interval.getUpper());
boolean found = false;
Collection<Point> tmpIntersections = intersections;
for (Point tmpIntersection : tmpIntersections) {
if (tmpIntersection.getX() > interval.getLower()
&& tmpIntersection.getX() < interval.getUpper()) {
intersectionsPoint = tmpIntersection.getX();
found = true;
break;
}
}
if (found) {
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);
}
splitActiveSlab(intersectionsPoint, interval);
//(d.) this may update sigma min
upperBound(intersectionsPoint);
//(e.) for i={1,2}, call lower bound(Ui)
lowerBound(subSlabU1);
lowerBound(subSlabU2);
} else {
this.intervals.poll();
if (subSlabU1.getActivity()) {
this.intervals.add(subSlabU1);
}
if (subSlabU2.getActivity()) {
this.intervals.add(subSlabU2);
}
}
} else {
@ -180,9 +168,8 @@ public class LeastMedianOfSquaresEstimator implements Algorithm {
* @param interval Intervall
* @return Anzahl der Schnittpunkte
*/
public int countInversions(Interval interval) {
return IntersectionComputer.getInstance()
.compute(setOfLines, interval.getLower(), interval.getUpper()).size();
public long countIntersections(Interval interval) {
return this.intersections.stream().filter(point -> point.getX().compareTo(interval.getLower()) > 0 && point.getX().compareTo(interval.getUpper()) <= 0).count();
}
@ -197,9 +184,7 @@ public class LeastMedianOfSquaresEstimator implements Algorithm {
//initialisiere die x-Queue mit den 2D Punkten und sortiere nach x-Lexikographischer Ordnung
List<Point> xQueue = new ArrayList<>();
Collection<Point> points = IntersectionComputer.getInstance()
.compute(setOfLines, interval.getLower(), interval.getUpper());
for (Point point : points) {
for (Point point : intersections) {
if (point.getX() >= interval.getLower() && point.getX() < interval.getUpper()) {
xQueue.add(point);
}
@ -216,8 +201,7 @@ public class LeastMedianOfSquaresEstimator implements Algorithm {
continue;
} else if (currentBracelet[0] < heightOfBracelet) {
heightOfBracelet = currentBracelet[0];
bracelet = new Line(current.getX(), current.getX(), currentBracelet[1],
currentBracelet[2]);
bracelet = new Line(current.getX(), current.getX(), currentBracelet[1], currentBracelet[2]);
}
}
@ -258,11 +242,9 @@ public class LeastMedianOfSquaresEstimator implements Algorithm {
if (height < heightsigmaMin) {
heightsigmaMin = height;
if (sigmaMin != null) {
sigmaMin.setEndPoints(point, sortedLineSequence.get(i)
, point, sortedLineSequence.get((i + kMinus) - 1));
sigmaMin = new Line(point, sortedLineSequence.get(i), point, sortedLineSequence.get((i + kMinus) - 1));
} else {
sigmaMin = new Line(point, point, sortedLineSequence.get(i),
sortedLineSequence.get((i + kMinus) - 1));
sigmaMin = new Line(point, point, sortedLineSequence.get(i), sortedLineSequence.get((i + kMinus) - 1));
}
}
}
@ -297,14 +279,16 @@ public class LeastMedianOfSquaresEstimator implements Algorithm {
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));
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())) {
Segment segment = line.getSegment(pslab);
Segment levelSegment = level.getSegment(pslab);
if ((segment.first().getY() < levelSegment.first().getY()) && (segment.second().getY() < levelSegment.second().getY())) {
alpha[i]++;
}
if ((line.getY1() > level.getY1()) && (line.getY2() > level.getY2())) {
if ((segment.first().getY() > levelSegment.first().getY()) && (segment.second().getY() > levelSegment.second().getY())) {
strictlyGreater++;
}
}
@ -373,10 +357,8 @@ public class LeastMedianOfSquaresEstimator implements Algorithm {
}
if (intersections.size() >= kMinusValue) {
Collections.sort(intersections);
double height = Math
.abs(intersections.get(0) - intersections.get(kMinusValue - 1));
return new Double[]{height, intersections.get(0),
intersections.get(kMinusValue - 1)};
double height = Math.abs(intersections.get(0) - intersections.get(kMinusValue - 1));
return new Double[]{height, intersections.get(0), intersections.get(kMinusValue - 1)};
} else {
return null;
}
@ -384,24 +366,17 @@ public class LeastMedianOfSquaresEstimator implements Algorithm {
}
private Line pepareResult() {
double m = (getSigmaMin().getX1() + getSigmaMin().getX2()) * 0.5;
double b = (getSigmaMin().getY1() + getSigmaMin().getY2() ) * 0.5;
slope = m * -1;
yInterception = b;
if (this.subscriber != null) {
double m = ((getSigmaMin().getX2() + getSigmaMin().getX1()) * 0.5);
double b = (getSigmaMin().getY2() + getSigmaMin().getY1()) * -0.5;
slope = m;
yInterception = b;
AlgorithmMessage data = new AlgorithmMessage();
data.setAlgorithmType(getType());
data.setType(SubscriberType.ALGORITHM);
data.setLineData(new Line(m, b));
data.setLineData(new Line(slope, yInterception));
this.subscriber.onNext(data);
} else {
double m = (getSigmaMin().getX2() + getSigmaMin().getX1()) * 0.5;
double b = (getSigmaMin().getY2() + getSigmaMin().getY1()) * -0.5;
slope = m;
yInterception = b;
}
return new Line(getSlope(), getYInterception());

View File

@ -13,6 +13,7 @@ import de.wwwu.awolf.presenter.util.IntersectionComputer;
import de.wwwu.awolf.presenter.util.Logging;
import de.wwwu.awolf.presenter.util.RandomSampler;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@ -39,9 +40,9 @@ public class RepeatedMedianEstimator implements Algorithm {
private Interval original;
//in der Literatur als L_i, C_i, und R_i bekannt
private int countLeftSlab;
private int countCenterSlab;
private int countRightSlab;
private long countLeftSlab;
private long countCenterSlab;
private long countRightSlab;
//in der Literatur als L_i, C_i, und R_i bekannt
private Set<Point> intersectionsInLeftSlab;
@ -49,7 +50,7 @@ public class RepeatedMedianEstimator implements Algorithm {
private Set<Point> intersectionsInRightSlab;
private double r;
private int n;
private long n;
private double k;
private double kLow;
private double kHigh;
@ -58,6 +59,7 @@ public class RepeatedMedianEstimator implements Algorithm {
private double yInterception;
private Flow.Subscriber<? super AlgorithmMessage> subscriber;
private Map<String, String> parameter;
private Set<Point> intersections;
public RepeatedMedianEstimator() {
parameter = new HashMap<>();
@ -96,6 +98,8 @@ public class RepeatedMedianEstimator implements Algorithm {
double thetaLow = 0;
double thetaHigh = 0;
intersections = IntersectionComputer.getInstance().compute(setOfLines);
while (countCenterSlab > 1) {
n = countCenterSlab;
r = Math.ceil(Math.pow(n, Double.parseDouble(parameter.get("Beta"))));
@ -163,18 +167,13 @@ public class RepeatedMedianEstimator implements Algorithm {
*/
public Double estimateMedianIntersectionAbscissas(List<Line> lines, Line sampledLine) {
List<Double> intersections = IntersectionComputer.getInstance()
.calculateIntersectionAbscissas(lines, sampledLine, original.getLower(),
original.getUpper());
List<Double> left = IntersectionComputer.getInstance()
.calculateIntersectionAbscissas(lines, sampledLine, original.getLower(),
interval.getLower());
List<Double> center = IntersectionComputer.getInstance()
.calculateIntersectionAbscissas(lines, sampledLine, interval.getLower(),
interval.getUpper());
//interval is the current interval
Set<Point> intersections = IntersectionComputer.getInstance().compute(lines);
long leftSize = intersections.stream().filter(p -> p.getX() <= interval.getLower()).count();
long centerSize = intersections.stream().filter(p -> p.getX() >= interval.getLower() && p.getX() < interval.getUpper()).count();
double ki = Math.ceil((n - 1) * 0.5) - left.size();
double i = (Math.ceil((Math.sqrt(n) * ki) / center.size()));
double ki = Math.ceil((n - 1) * 0.5) - leftSize;
double i = (Math.ceil((Math.sqrt(n) * ki) / centerSize));
int accessIndex;
if (i < 0) {
accessIndex = 0;
@ -183,8 +182,8 @@ public class RepeatedMedianEstimator implements Algorithm {
} else {
accessIndex = (int) i;
}
return FastElementSelector.randomizedSelect(intersections, accessIndex);
LinkedList<Point> points = new LinkedList<>(intersections);
return FastElementSelector.randomizedSelect(points, accessIndex).getX();
}
/**
@ -202,20 +201,10 @@ public class RepeatedMedianEstimator implements Algorithm {
* 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(final double lower, final double upper) {
IntersectionComputer instance = IntersectionComputer.getInstance();
intersectionsInLeftSlab = new HashSet<>(
instance.compute(setOfLines, interval.getLower(), lower));
intersectionsInCenterSlab = new HashSet<>(instance.compute(setOfLines, lower, upper));
intersectionsInRightSlab = new HashSet<>(
instance.compute(setOfLines, upper, interval.getUpper()));
int tmp = new HashSet<>(
instance.compute(setOfLines, interval.getLower(), interval.getUpper()))
.size();
countLeftSlab = intersectionsInLeftSlab.size();
countCenterSlab = intersectionsInCenterSlab.size();
countRightSlab = intersectionsInRightSlab.size();
countLeftSlab = intersections.stream().filter(point -> point.getX().compareTo(lower) < 0).count();
countCenterSlab = intersections.stream().filter(point -> point.getX().compareTo(lower) >= 0 && point.getX().compareTo(upper) < 0).count();
countRightSlab = intersections.stream().filter(point -> point.getX().compareTo(upper) >= 0).count();
}
/**
@ -245,7 +234,7 @@ public class RepeatedMedianEstimator implements Algorithm {
}
private Line pepareResult(final double thetaLow, final double thetaHigh) {
slope = thetaLow;
slope = thetaLow * -1;
List<Double> potentialYInterceptions = new ArrayList<>();
setOfLines.forEach(line -> {
potentialYInterceptions.add(line.getB() - (slope * line.getM()));
@ -268,7 +257,7 @@ public class RepeatedMedianEstimator implements Algorithm {
/**
* @return Anzahl der Geraden
*/
public Integer getN() {
public Long getN() {
return n;
}

View File

@ -14,12 +14,14 @@ import de.wwwu.awolf.presenter.util.IntersectionComputer;
import de.wwwu.awolf.presenter.util.RandomSampler;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Flow;
import java.util.stream.Collectors;
/**
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
@ -30,12 +32,11 @@ import java.util.concurrent.Flow;
*/
public class TheilSenEstimator implements Algorithm {
private static final Algorithm.Type type = Type.TS;
private List<Line> setOfLines;
private double r;
private double N;
private double intervalSize;
private int k;
//Intervall und die temporaeren Grenzen
private Interval interval;
@ -45,6 +46,7 @@ public class TheilSenEstimator implements Algorithm {
private double yInterception;
private Flow.Subscriber<? super AlgorithmMessage> subscriber;
private Map<String, String> parameter;
private List<Point> intersections;
/**
@ -53,62 +55,53 @@ public class TheilSenEstimator implements Algorithm {
*/
public Line call() {
int n = this.setOfLines.size();
this.N = BinomialCoeffizient.run(n, 2);
int numberOfLines = this.setOfLines.size();
this.intervalSize = BinomialCoeffizient.run(numberOfLines, 2);
//this.k = Integer.valueOf((int) (N * 0.5)) - 1;
this.k = (int) (N / 2);
this.k = (int) (intervalSize / 2);
double POSITIV_INF = 9999.0;
double NEGATIV_INF = -9999.0;
interval = new Interval(NEGATIV_INF, POSITIV_INF);
interval = new Interval(Double.MIN_VALUE, Double.MAX_VALUE);
//damit eine initiale Ordnung herscht
//Collections.sort(intervalIntersections);
r = n;
List<Point> intervalIntersections = new LinkedList<>(IntersectionComputer.getInstance()
.compute(setOfLines, interval.getLower(), interval.getUpper()));
r = numberOfLines;
intersections = new LinkedList<>(IntersectionComputer.getInstance().compute(setOfLines));
while (true) {
double EPSILON = 0.00001;
if (this.N <= n
|| (Math.abs(interval.getUpper() - interval.getLower())) < EPSILON) {
if (this.intervalSize <= numberOfLines || (Math.abs(interval.getUpper() - interval.getLower())) < EPSILON) {
break;
} else {
//Anzahl der Schnittpunkte im Intervall [-Inf, a)
int numberOfIntersections = getIntervalSize(NEGATIV_INF,
interval.getLower());
int numberOfIntersections = getIntervalSize(Double.MIN_VALUE, interval.getLower());
//Randomized Interpolating Search
//Hilfsvariablen (siehe original Paper)
double j = (r / N) * (double) (k - numberOfIntersections);
double j = (r / intervalSize) * (double) (k - numberOfIntersections);
int jA = (int) Math.max(1, Math.floor(j - (1.5 * Math.sqrt(r))));
int jB = (int) Math.min(r, Math.floor(j + (1.5 * Math.sqrt(r))));
/* Suche nach einem passenderen und kleineren Intervall
Schleife terminiert wenn die das k-te Elemnet zwischen aVariant und bVariant liegt und
das Intrvall weniger als 11*N / sqrt(r) Elemente besitzt */
List<Point> sampledIntersections = new LinkedList<>();
do {
//zufällige Stichprobe
List<Point> sampledIntersections = RandomSampler
.run(intervalIntersections, r);
Collections.shuffle(intersections);
for(int i=0; i < r; i++) {
sampledIntersections.add(intersections.get(i % intersections.size()));
}
aVariant = FastElementSelector
.randomizedSelect(getIntersectionAbscissas(sampledIntersections),
jA);
bVariant = FastElementSelector
.randomizedSelect(getIntersectionAbscissas(sampledIntersections),
jB);
} while (!checkCondition(NEGATIV_INF));
aVariant = FastElementSelector.randomizedSelect(getIntersectionAbscissas(sampledIntersections), jA);
bVariant = FastElementSelector.randomizedSelect(getIntersectionAbscissas(sampledIntersections), jB);
} while (!checkCondition(sampledIntersections));
interval.setLower(aVariant);
interval.setUpper(bVariant);
intervalIntersections = getOpenIntervalElements(interval.getLower(),
interval.getUpper());
N = getIntervalSize(interval.getLower(), interval.getUpper());
intersections = getOpenIntervalElements(interval.getLower(), interval.getUpper());
intervalSize = getIntervalSize(interval.getLower(), interval.getUpper());
}
}
return pepareResult();
return prepareResults();
}
@Override
@ -139,19 +132,13 @@ public class TheilSenEstimator implements Algorithm {
*
* @return Boolscher Wert ob die Bedingung erfüllt ist
*/
private Boolean checkCondition(final double lowerBound) {
//Double kthElement = FastElementSelector.randomizedSelect(xCoordinates, k);
//Boolean cond1 = (kthElement > aVariant) && (kthElement <= bVariant);
private Boolean checkCondition(List<Point> sampledIntersections) {
Point kthElement = FastElementSelector.randomizedSelect(sampledIntersections, k);
Boolean cond1 = (kthElement.getX() > aVariant) && (kthElement.getX() <= bVariant);
int lowerCount = getIntervalSize(lowerBound, aVariant);
int higherCount = getIntervalSize(lowerBound, bVariant);
int count = getIntervalSize(aVariant, bVariant);
Boolean conda = k > lowerCount;
Boolean condb = k <= higherCount;
Boolean cond1 = conda && condb;
Boolean cond2 = (higherCount - lowerCount) <= ((11 * N) / Math.sqrt(r));
Boolean cond2 = count <= ((11 * intervalSize) / Math.sqrt(r));
return (cond1 && cond2) || (aVariant == bVariant);
}
@ -166,7 +153,10 @@ public class TheilSenEstimator implements Algorithm {
* @return Anzahl der Schnittpunkte im Interval [a,b)
*/
public int getIntervalSize(double a, double b) {
return getOpenIntervalElements(a, b).size();
if (a == b)
return 0;
else
return getOpenIntervalElements(a, b).size();
}
/**
@ -179,45 +169,22 @@ public class TheilSenEstimator implements Algorithm {
* @return Liste der Schnittpunkte die im Interval (a,b) vertreten sind
*/
public List<Point> getOpenIntervalElements(double a, double b) {
Collection<Point> intersections = IntersectionComputer.getInstance()
.compute(setOfLines, a, b);
return new ArrayList<>(intersections);
return intersections.stream().filter(point -> point.getX().compareTo(a) >= 0 && point.getX() < 0).collect(Collectors.toList());
}
private Line pepareResult() {
double m, x;
double b, y;
List<Point> resultSt = getOpenIntervalElements(interval.getLower(),
interval.getUpper());
List<Double> resultAbscissas = new ArrayList<>();
for (Point p : resultSt) {
resultAbscissas.add(p.getX());
}
List<Double> yCoords = new ArrayList<>();
for (Point p : getOpenIntervalElements(interval.getLower(), interval.getUpper())) {
yCoords.add(p.getY());
}
//TODO
private Line prepareResults() {
List<Point> intervalElements = getOpenIntervalElements(interval.getLower(), interval.getUpper());
double pseudoIndex = getIntervalSize(-9999.0, interval.getLower()) * 1.0;
m = FastElementSelector.randomizedSelect(resultAbscissas, k - pseudoIndex);
Set<Double> unique = new LinkedHashSet<>(yCoords);
yCoords.clear();
yCoords.addAll(unique);
b = FastElementSelector.randomizedSelect(yCoords, yCoords.size() * 0.5) * -1;
slope = m;
double m = FastElementSelector.randomizedSelect(intervalElements, k - pseudoIndex).getX();
double b = FastElementSelector.randomizedSelect(intervalElements, intervalElements.size() * 0.5).getY() * -1;
slope = m * -1;
yInterception = b;
if (this.subscriber != null) {
AlgorithmMessage data = new AlgorithmMessage();
data.setAlgorithmType(getType());
data.setType(SubscriberType.ALGORITHM);
data.setLineData(new Line(m, b));
data.setLineData(new Line(slope, yInterception));
this.subscriber.onNext(data);
}

View File

@ -139,7 +139,7 @@ public class NaiveLeastMedianOfSquaresEstimator implements Algorithm {
* @return Steigung
*/
public Double getSlope() {
return m * -1;
return m;
}
@Override

View File

@ -163,7 +163,7 @@ public class NaiveRepeatedMedianEstimator implements Algorithm {
* @return Steigung
*/
public double getSlope() {
return medianX * -1;
return medianX;
}
/**

View File

@ -114,7 +114,7 @@ public class NaiveTheilSenEstimator implements Algorithm {
* @return Steigung
*/
public double getSlope() {
return slope * -1;
return slope;
}
/**

View File

@ -37,7 +37,10 @@ public class CloudDatasetGenerator implements Callable<Set<Line>> {
double signal = m * i + b;
signal *= -1;
Line line = new Line(i, signal - y);
double min = -10000d;
double max = 10000d;
Line line = new Line(min, max, i * min + signal - y, i * max + signal - y);
line.setId(i - 1 + "");
lines.add(line);
}

View File

@ -44,7 +44,6 @@ public class DatasetGenerator {
while (size < n) {
double y = random.nextGaussian();
double signal = m * y + b;
signal *= -1;
if (!points.containsKey(y)) {
points.put(y, signal);
@ -52,12 +51,14 @@ public class DatasetGenerator {
}
}
int idx = lines.size();
double min = -10000d;
double max = 10000d;
int idx = 0;
for (Map.Entry<Double, Double> d : points.entrySet()) {
Line line = new Line(d.getKey(), d.getValue());
line.setId(idx + "");
Line line = new Line(min, max, d.getKey() * min + d.getValue(), d.getKey() * max + d.getValue());
line.setId(idx++ + "");
lines.add(line);
idx++;
}
return lines;

View File

@ -0,0 +1,193 @@
package de.wwwu.awolf.presenter.util;
import de.wwwu.awolf.model.dao.Interval;
import de.wwwu.awolf.model.dao.Line;
import de.wwwu.awolf.model.dao.Line.Segment;
import de.wwwu.awolf.model.dao.Point;
import de.wwwu.awolf.model.dao.Event;
import java.util.*;
/**
* source code:
* https://github.com/valenpe7/bentley-ottmann
* Created by valen_000 on 14. 5. 2017.
*/
public class BentleyOttmann {
private Queue<Event> Q;
private NavigableSet<Segment> T;
private Set<Point> X;
private Collection<Line> lines;
BentleyOttmann(Collection<Line> lines) {
this.Q = new PriorityQueue<>(new event_comparator());
this.T = new TreeSet<>(new segment_comparator());
this.X = new HashSet<>();
this.lines = lines;
lines.forEach(line -> {
Segment segment = line.getSegment();
this.Q.add(new Event(segment.first(), segment, 0));
this.Q.add(new Event(segment.second(), segment, 1));
});
}
public void find_intersections() {
while(!this.Q.isEmpty()) {
Event e = this.Q.poll();
double L = e.get_value();
switch(e.get_type()) {
case 0:
for(Segment s : e.get_segments()) {
this.recalculate(L);
this.T.add(s);
if(this.T.lower(s) != null) {
Segment r = this.T.lower(s);
this.report_intersection(r, s, L);
}
if(this.T.higher(s) != null) {
Segment t = this.T.higher(s);
this.report_intersection(t, s, L);
}
if(this.T.lower(s) != null && this.T.higher(s) != null) {
Segment r = this.T.lower(s);
Segment t = this.T.higher(s);
this.remove_future(r, t);
}
}
break;
case 1:
for(Segment s : e.get_segments()) {
if(this.T.lower(s) != null && this.T.higher(s) != null) {
Segment r = this.T.lower(s);
Segment t = this.T.higher(s);
this.report_intersection(r, t, L);
}
this.T.remove(s);
}
break;
case 2:
Segment s_1 = e.get_segments().get(0);
Segment s_2 = e.get_segments().get(1);
this.swap(s_1, s_2);
if(s_1.get_value() < s_2.get_value()) {
if(this.T.higher(s_1) != null) {
Segment t = this.T.higher(s_1);
this.report_intersection(t, s_1, L);
this.remove_future(t, s_2);
}
if(this.T.lower(s_2) != null) {
Segment r = this.T.lower(s_2);
this.report_intersection(r, s_2, L);
this.remove_future(r, s_1);
}
} else {
if(this.T.higher(s_2) != null) {
Segment t = this.T.higher(s_2);
this.report_intersection(t, s_2, L);
this.remove_future(t, s_1);
}
if(this.T.lower(s_1) != null) {
Segment r = this.T.lower(s_1);
this.report_intersection(r, s_1, L);
this.remove_future(r, s_2);
}
}
this.X.add(e.get_point());
break;
}
}
}
private boolean report_intersection(Segment s_1, Segment s_2, double L) {
double x1 = s_1.first().getX();
double y1 = s_1.first().getY();
double x2 = s_1.second().getX();
double y2 = s_1.second().getY();
double x3 = s_2.first().getX();
double y3 = s_2.first().getY();
double x4 = s_2.second().getX();
double y4 = s_2.second().getY();
double r = (x2 - x1) * (y4 - y3) - (y2 - y1) * (x4 - x3);
if(r != 0) {
double t = ((x3 - x1) * (y4 - y3) - (y3 - y1) * (x4 - x3)) / r;
double u = ((x3 - x1) * (y2 - y1) - (y3 - y1) * (x2 - x1)) / r;
if(t >= 0 && t <= 1 && u >= 0 && u <= 1) {
double x_c = x1 + t * (x2 - x1);
double y_c = y1 + t * (y2 - y1);
if(x_c > L) {
this.Q.add(new Event(new Point(x_c, y_c), new ArrayList<>(Arrays.asList(s_1, s_2)), 2));
return true;
}
}
}
return false;
}
private boolean remove_future(Segment s_1, Segment s_2) {
for(Event e : this.Q) {
if(e.get_type() == 2) {
if((e.get_segments().get(0) == s_1 && e.get_segments().get(1) == s_2) || (e.get_segments().get(0) == s_2 && e.get_segments().get(1) == s_1)) {
this.Q.remove(e);
return true;
}
}
}
return false;
}
private void swap(Segment s_1, Segment s_2) {
this.T.remove(s_1);
this.T.remove(s_2);
double value = s_1.get_value();
s_1.set_value(s_2.get_value());
s_2.set_value(value);
this.T.add(s_1);
this.T.add(s_2);
}
private void recalculate(double L) {
Iterator<Segment> iter = this.T.iterator();
while(iter.hasNext()) {
iter.next().calculate_value(L);
}
}
public void print_intersections() {
for(Point p : this.X) {
Logging.logInfo("Intersection: (" + p.getX() + ", " + p.getY() + ")");
}
}
public Set<Point> get_intersections() {
print_intersections();
return this.X;
}
private class event_comparator implements Comparator<Event> {
@Override
public int compare(Event e_1, Event e_2) {
if(e_1.get_value() > e_2.get_value()) {
return 1;
}
if(e_1.get_value() < e_2.get_value()) {
return -1;
}
return 0;
}
}
private class segment_comparator implements Comparator<Segment> {
@Override
public int compare(Segment s_1, Segment s_2) {
if(s_1.get_value() < s_2.get_value()) {
return 1;
}
if(s_1.get_value() > s_2.get_value()) {
return -1;
}
return 0;
}
}
}

View File

@ -1,19 +0,0 @@
package de.wwwu.awolf.presenter.util.Comparators;
import de.wwwu.awolf.model.dao.Line;
import java.util.Comparator;
/**
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
*
* @Author: Armin Wolf
* @Email: a_wolf28@uni-muenster.de
* @Date: 19.06.2017.
*/
public class YOrderLineComparatorBegin implements Comparator<Line> {
@Override
public int compare(Line o1, Line o2) {
return o1.getY1().compareTo(o2.getY1());
}
}

View File

@ -1,19 +0,0 @@
package de.wwwu.awolf.presenter.util.Comparators;
import de.wwwu.awolf.model.dao.Line;
import java.util.Comparator;
/**
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
*
* @Author: Armin Wolf
* @Email: a_wolf28@uni-muenster.de
* @Date: 19.06.2017.
*/
public class YOrderLineComparatorEnd implements Comparator<Line> {
@Override
public int compare(Line o1, Line o2) {
return o1.getY2().compareTo(o2.getY2());
}
}

View File

@ -1,6 +1,7 @@
package de.wwwu.awolf.presenter.util;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -20,7 +21,7 @@ public class FastElementSelector {
* @param i Rang des gewünschten Elements
* @return das Element
*/
public static Double randomizedSelect(List<Double> a, double i) {
public static <Object extends Comparable<Object>> Object randomizedSelect(List<? extends Object> a, double i) {
int start = 0;
int end = a.size() - 1;
@ -57,7 +58,7 @@ public class FastElementSelector {
* @param end Index des letzten Elements
* @return Pivotelement
*/
private static int randomizedPartition(List<Double> a, int start, int end) {
private static <Object extends Comparable<Object>> int randomizedPartition(List<? extends Object> a, int start, int end) {
int i = 0;
SecureRandom random = new SecureRandom();
@ -80,11 +81,11 @@ public class FastElementSelector {
* @param end Index des letzten Elements
* @return Pivotelement
*/
private static int partition(List<Double> a, int start, int end) {
Double x = a.get(end);
private static <Object extends Comparable<Object>> int partition(List<? extends Object> a, int start, int end) {
Object x = a.get(end);
int i = start - 1;
for (int j = start; j <= end - 1; j++) {
if (a.get(j) <= x) {
if (a.get(j).compareTo(x) < 0) {
i++;
Collections.swap(a, i, j);
}

View File

@ -1,6 +1,7 @@
package de.wwwu.awolf.presenter.util;
import com.google.common.collect.Lists;
import de.wwwu.awolf.model.dao.Interval;
import de.wwwu.awolf.model.dao.Line;
import de.wwwu.awolf.model.dao.Point;
import java.util.ArrayList;
@ -46,128 +47,11 @@ public class IntersectionComputer {
* Berechnet zu einer gegebenen Menge von dualen Geraden die Schnittpunkte. Dafür wird ein modifizierter Merge-Sort Algorithmus verwendett. Um die Performance zu steigern wird die Berechnung ab
* einer passenden Größe auf vier Threads ausgelagert.
*
* @param lower untere Schranke
* @param higher obere Schranke
* @return Liste der Schnittpunkte
*/
public Set<Point> compute(final Collection<Line> lines, final double lower,
final double higher) {
final Set<Line> fullInput = new HashSet<>(lines);
final Set<Line> copyInput = new HashSet<>(lines);
if (lower == higher) {
return Collections.emptySet();
} else {
Logging.logDebug(
"Open ForkJoinPool: lines: " + lines.size() + " I(" + lower + ", " + higher
+ "]");
ForkJoinPool pool = ForkJoinPool.commonPool();
RecursiveComputationTask recursiveComputationTask = new RecursiveComputationTask(
fullInput,
copyInput, lower, higher);
pool.execute(recursiveComputationTask);
Set<Point> join = recursiveComputationTask.join();
return join;
}
public Set<Point> compute(final Collection<Line> lines) {
BentleyOttmann bentleyOttmann = new BentleyOttmann(lines);
bentleyOttmann.find_intersections();
return bentleyOttmann.get_intersections();
}
/**
* Berechnet die Schnittpunkte zwischen einer gegebenen Gerade und einer Menge an Geraden.
*
* @param set Menge an Geraden
* @param sampledLine eine spezielle Gerade
* @return Liste mit x Koordinaten der Schnittpunkte
*/
public List<Double> calculateIntersectionAbscissas(Collection<Line> set, Line sampledLine,
double lower, double upper) {
List<Line> lines = new LinkedList<>(set);
Set<Double> intersections = new HashSet<>();
for (Line line : lines) {
if (line != sampledLine) {
if (sampledLine.doIntersect(line, lower, upper)) {
intersections.add(sampledLine.intersect(line).getX());
}
}
}
return new ArrayList<>(intersections);
}
private class RecursiveComputationTask extends RecursiveTask<Set<Point>> {
private static final int THRESHOLD = 200;
private final Set<Line> lines;
private final Set<Line> fullList;
private final double lower;
private final double upper;
public RecursiveComputationTask(final Set<Line> fullList, final Set<Line> lines,
final double lower, final double upper) {
this.lines = lines;
this.fullList = fullList;
this.lower = lower;
this.upper = upper;
}
@Override
protected Set<Point> compute() {
if (this.lines.isEmpty()) {
return Collections.emptySet();
} else if (this.lines.size() > THRESHOLD) {
return ForkJoinTask.invokeAll(createSubTask()).stream()
.map(ForkJoinTask::join)
.flatMap(Collection::stream).collect(Collectors.toSet());
} else {
return work(this.fullList, this.lines, this.lower, this.upper);
}
}
private Collection<RecursiveComputationTask> createSubTask() {
List<RecursiveComputationTask> dividedTasks = new ArrayList<>();
long midpoint = Math.round(this.lines.size() * 0.5);
Set<Line> firstSubSet = new HashSet<>();
Set<Line> secondSubSet = new HashSet<>();
AtomicInteger count = new AtomicInteger();
this.lines.forEach(next -> {
int index = count.getAndIncrement();
if (index < midpoint) {
firstSubSet.add(next);
} else {
secondSubSet.add(next);
}
});
dividedTasks
.add(new RecursiveComputationTask(this.fullList, firstSubSet, this.lower,
this.upper));
dividedTasks
.add(new RecursiveComputationTask(this.fullList, secondSubSet, this.lower,
this.upper));
return dividedTasks;
}
private Set<Point> work(Set<Line> fullList, Set<Line> lines, double lower,
double higher) {
Set<Point> points = new HashSet<>();
List<List<Line>> lists = Lists
.cartesianProduct(new ArrayList<>(fullList), new ArrayList<>(lines));
lists.forEach(entry -> {
if (entry.get(0).doIntersect(entry.get(1), lower, upper)) {
Point intersect = entry.get(0).intersect(entry.get(1));
if (intersect.getX() > lower && intersect.getX() <= higher) {
points.add(intersect);
}
}
});
return new HashSet<>(points);
}
}
}

View File

@ -95,10 +95,10 @@ public class AlgorithmTabController {
if (pLine != null) {
lineSerie.getData().clear();
Double x1 = pLine.calculateX1(model.getMin());
Double y1 = pLine.calculateY1(model.getMin());
Double x2 = pLine.calculateX2(model.getMax());
Double y2 = pLine.calculateY2(model.getMax());
Double x1 = model.getMin();
Double y1 = model.getMin() * pLine.getM() + pLine.getB();
Double x2 = model.getMax();
Double y2 = model.getMax() * pLine.getM() + pLine.getB();
lineSerie.getData().add(new XYChart.Data<>(x1, y1));
lineSerie.getData().add(new XYChart.Data<>(x2, y2));