fixed the issue with rm estimator
This commit is contained in:
parent
2a003fee9a
commit
bef956bf4c
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -4,8 +4,8 @@
|
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>wwu</groupId>
|
||||
<artifactId>Masterarbeit</artifactId>
|
||||
<groupId>de.wwu.awolf</groupId>
|
||||
<artifactId>masterarbeit</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<build>
|
||||
|
@ -13,13 +13,13 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
||||
<!-- Maven Assembly Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
@ -49,21 +49,6 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.googlecode.mavennatives</groupId>
|
||||
<artifactId>maven-nativedependencies-plugin</artifactId>
|
||||
<version>0.0.7</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpacknatives</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.sonarsource.scanner.maven</groupId>
|
||||
<artifactId>sonar-maven-plugin</artifactId>
|
||||
|
@ -88,20 +73,6 @@
|
|||
<version>4.12</version>
|
||||
</dependency>
|
||||
|
||||
<!-- OpenCSV-->
|
||||
<dependency>
|
||||
<groupId>com.opencsv</groupId>
|
||||
<artifactId>opencsv</artifactId>
|
||||
<version>3.9</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JTattoo Look and Feel -->
|
||||
<dependency>
|
||||
<groupId>com.jtattoo</groupId>
|
||||
<artifactId>JTattoo</artifactId>
|
||||
<version>1.6.11</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
|
@ -109,12 +80,6 @@
|
|||
<version>2.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openpnp</groupId>
|
||||
<artifactId>opencv</artifactId>
|
||||
<version>3.2.0-1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
|
@ -122,13 +87,16 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.4</version>
|
||||
<groupId>com.opencsv</groupId>
|
||||
<artifactId>opencsv</artifactId>
|
||||
<version>5.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>28.2-jre</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
|
@ -51,10 +51,26 @@ public class Line {
|
|||
this.m = m;
|
||||
this.b = b;
|
||||
|
||||
this.x1 = MIN;
|
||||
this.y1 = (MIN * m) + b;
|
||||
this.x2 = MAX * 0.5;
|
||||
this.y2 = ((MAX * 0.5) * m) + b;
|
||||
this.x1 = calculateX1(MIN);
|
||||
this.y1 = calculateY1(MIN);
|
||||
this.x2 = calculateX2(MAX * 0.5);
|
||||
this.y2 = calculateY2(MAX * 0.5);
|
||||
}
|
||||
|
||||
private Double calculateX1(Double min) {
|
||||
return (double) min;
|
||||
}
|
||||
|
||||
private Double calculateY1(Double min) {
|
||||
return (min * m) + b;
|
||||
}
|
||||
|
||||
private Double calculateX2(Double max) {
|
||||
return (double) max;
|
||||
}
|
||||
|
||||
private Double calculateY2(Double max) {
|
||||
return (max * m) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -188,4 +204,74 @@ public class Line {
|
|||
public String toString() {
|
||||
return "Line m: " + this.getM() + ", b: " + this.getB();
|
||||
}
|
||||
|
||||
|
||||
public Point intersect(Line line) {
|
||||
double x = (line.b - this.b) / (this.m - line.m);
|
||||
double y = this.m * x + this.b;
|
||||
|
||||
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) {
|
||||
if (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()))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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
|
||||
if (o4 == 0 && onSegment(p2, q1, q2)) return true;
|
||||
|
||||
return false; // Doesn't fall in any of the above cases
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import java.util.List;
|
|||
*/
|
||||
public class LineModel {
|
||||
|
||||
private List<Point> nodes;
|
||||
private List<Line> lines;
|
||||
private Double xMinimum;
|
||||
private Double xMaximum;
|
||||
|
@ -24,7 +23,6 @@ public class LineModel {
|
|||
* Konstruktor
|
||||
*/
|
||||
public LineModel() {
|
||||
nodes = new ArrayList<>();
|
||||
lines = new LinkedList<>();
|
||||
|
||||
xMinimum = Double.MAX_VALUE;
|
||||
|
@ -34,14 +32,6 @@ public class LineModel {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fügt einen Schnittpunkt zum Modell hinzu
|
||||
*
|
||||
* @param node Schnittpunkt
|
||||
*/
|
||||
public void addNode(Point node) {
|
||||
this.nodes.add(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fügt eine Gerade zu dem Modell hinzu
|
||||
|
@ -52,19 +42,6 @@ public class LineModel {
|
|||
this.lines.add(line);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Liste der Schnittpunkte
|
||||
*/
|
||||
public List<Point> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nodes Liste der Schnittpunkte
|
||||
*/
|
||||
public void setNodes(List<Point> nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Liste der Geraden
|
||||
|
|
|
@ -114,4 +114,8 @@ public class Point implements Comparable<Point> {
|
|||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -90,47 +90,6 @@ public abstract class AbstractPresenter implements Flow.Subscriber<Data> {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Startet das parallele Berechnen der Schnittpunkte der Geraden die im Modell enthalten sind.
|
||||
*/
|
||||
private void startIntersectionCalculation() {
|
||||
getExecutor().submit(() -> {
|
||||
getModel().resetRanges();
|
||||
IntersectionComputer intersectionComputer = new IntersectionComputer(getModel().getLines());
|
||||
getModel().setNodes(intersectionComputer.compute());
|
||||
|
||||
getModel().setxMaximum(intersectionComputer.getxMaximum());
|
||||
getModel().setxMinimum(intersectionComputer.getxMinimum());
|
||||
getModel().setyMaximum(intersectionComputer.getyMaximum());
|
||||
getModel().setyMinimum(intersectionComputer.getyMinimum());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper Methode die das berechnen der Schnittpunkte anstößt und die Ergebnisse(Anzahl der Schnittpunkte)
|
||||
* visuell darstellt.
|
||||
*/
|
||||
protected void computeIntersections() {
|
||||
getExecutor().submit(() -> {
|
||||
long start, end;
|
||||
start = System.currentTimeMillis();
|
||||
startIntersectionCalculation();
|
||||
end = System.currentTimeMillis();
|
||||
Logging.logInfo("Computing intersections took " + (end - start) / 1000 + "ms");
|
||||
});
|
||||
|
||||
//darstellung der Ergebnisse
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
getView().enableFunctionality();
|
||||
getView().getProgressDialog().dispose();
|
||||
});
|
||||
|
||||
Logging.logInfo("Informationen zu dem aktuellen Modell");
|
||||
Logging.logInfo("Anzahl der Geraden: " + getModel().getLines().size() + ".");
|
||||
Logging.logInfo("Anzahl der Schnittpunkte: " + getModel().getNodes().size() + ".");
|
||||
Logging.logInfo("Import war Erfolgreich!");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return das zu grunde legende Modell
|
||||
*/
|
||||
|
|
|
@ -63,10 +63,6 @@ public class Presenter extends AbstractPresenter {
|
|||
}
|
||||
|
||||
|
||||
public void visualizeDualLines() {
|
||||
getView().createDualityDialog();
|
||||
}
|
||||
|
||||
/***************************************************************************************************************************
|
||||
* Ausführung der Algorithmen
|
||||
***************************************************************************************************************************/
|
||||
|
@ -81,7 +77,7 @@ public class Presenter extends AbstractPresenter {
|
|||
//Parameter für den Algortihmus
|
||||
Double constant = Double.parseDouble(input[0]);
|
||||
double error = Double.parseDouble(input[1]);
|
||||
LeastMedianOfSquaresEstimator lms = new LeastMedianOfSquaresEstimator(getModel().getLines(), getModel().getNodes(), this);
|
||||
LeastMedianOfSquaresEstimator lms = new LeastMedianOfSquaresEstimator(getModel().getLines(), this);
|
||||
//setzen der Parameter
|
||||
lms.setConstant(constant);
|
||||
lms.setQuantileError(error);
|
||||
|
@ -114,7 +110,7 @@ public class Presenter extends AbstractPresenter {
|
|||
*/
|
||||
public void calculateTS(String input) {
|
||||
if (input != null) {
|
||||
TheilSenEstimator ts = new TheilSenEstimator(getModel().getLines(), getModel().getNodes(), this);
|
||||
TheilSenEstimator ts = new TheilSenEstimator(getModel().getLines(), this);
|
||||
//Presenter soll die Klasse überwachen
|
||||
getExecutor().submit(ts);
|
||||
}
|
||||
|
@ -131,12 +127,10 @@ public class Presenter extends AbstractPresenter {
|
|||
*/
|
||||
public void startImport(File file) {
|
||||
List<Line> data = getDataProvider().getData(file);
|
||||
if (data != null) {
|
||||
//Berechnung der Schnittpunkte und vis. der Ergebnisse (anz. Geraden, anz. Schnittpunkte)
|
||||
getModel().setLines(data);
|
||||
computeIntersections();
|
||||
}
|
||||
|
||||
//Berechnung der Schnittpunkte und vis. der Ergebnisse (anz. Geraden, anz. Schnittpunkte)
|
||||
getModel().setLines(data);
|
||||
getView().enableFunctionality();
|
||||
Logging.logInfo("Import successfully! " + Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -166,8 +160,8 @@ public class Presenter extends AbstractPresenter {
|
|||
public void generateDataset(int n, DataProvider.DataType type) {
|
||||
List<Line> data = getDataProvider().getData(type, n);
|
||||
getModel().setLines(data);
|
||||
computeIntersections();
|
||||
getView().enableFunctionality();
|
||||
Logging.logInfo("Generated successfully!" + Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,7 @@ import de.wwwu.awolf.model.communication.Data;
|
|||
import de.wwwu.awolf.model.communication.SubscriberType;
|
||||
import de.wwwu.awolf.presenter.Presenter;
|
||||
import de.wwwu.awolf.presenter.algorithms.Algorithm;
|
||||
import de.wwwu.awolf.presenter.util.IntersectionCounter;
|
||||
import de.wwwu.awolf.presenter.util.IntersectionComputer;
|
||||
import de.wwwu.awolf.presenter.util.Logging;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -23,9 +23,7 @@ import java.util.concurrent.Flow;
|
|||
*/
|
||||
public class LeastMedianOfSquaresEstimator implements Algorithm, Flow.Publisher<Data> {
|
||||
|
||||
private List<Line> set;
|
||||
private List<Point> intersections;
|
||||
private IntersectionCounter invCounter = new IntersectionCounter();
|
||||
private List<Line> setOfLines;
|
||||
private int n;
|
||||
private double quantileError;
|
||||
private int kPlus;
|
||||
|
@ -45,16 +43,13 @@ public class LeastMedianOfSquaresEstimator implements Algorithm, Flow.Publisher<
|
|||
/**
|
||||
* Konstruktor
|
||||
*
|
||||
* @param set Liste der Geraden
|
||||
* @param intersections Liste der Schnittpunkte
|
||||
* @param setOfLines Liste der Geraden
|
||||
* @param presenter Presenter (Beobachter)
|
||||
*/
|
||||
public LeastMedianOfSquaresEstimator(List<Line> set, List<Point> intersections,
|
||||
Presenter presenter) {
|
||||
this.set = set;
|
||||
this.intersections = intersections;
|
||||
public LeastMedianOfSquaresEstimator(List<Line> setOfLines, Presenter presenter) {
|
||||
this.setOfLines = setOfLines;
|
||||
|
||||
n = set.size();
|
||||
n = setOfLines.size();
|
||||
double qPlus = 0.5;
|
||||
quantileError = 0.1;
|
||||
double qMinus = qPlus * (1 - quantileError);
|
||||
|
@ -66,11 +61,10 @@ public class LeastMedianOfSquaresEstimator implements Algorithm, Flow.Publisher<
|
|||
/**
|
||||
* Konstruktor
|
||||
*
|
||||
* @param set Liste der Geraden
|
||||
* @param intersections Liste der Schnittpunkte
|
||||
* @param setOfLines Liste der Geraden
|
||||
*/
|
||||
public LeastMedianOfSquaresEstimator(List<Line> set, List<Point> intersections) {
|
||||
this(set, intersections, null);
|
||||
public LeastMedianOfSquaresEstimator(List<Line> setOfLines) {
|
||||
this(setOfLines, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,9 +95,8 @@ public class LeastMedianOfSquaresEstimator implements Algorithm, Flow.Publisher<
|
|||
}
|
||||
};
|
||||
intervals = new PriorityQueue<>(comparator);
|
||||
intervals.add(new Interval(-100000, 100000));
|
||||
intervals.add(new Interval(-10000, 10000));
|
||||
heightsigmaMin = Double.MAX_VALUE;
|
||||
List<Point> tmpIntersections = new ArrayList<>(intersections);
|
||||
|
||||
//(3.) Apply the following steps as long as the exists active intervals
|
||||
boolean active = true;
|
||||
|
@ -120,7 +113,7 @@ public class LeastMedianOfSquaresEstimator implements Algorithm, Flow.Publisher<
|
|||
} else {
|
||||
//(c.) otherwise....
|
||||
// get random intersections point...
|
||||
Collections.shuffle(tmpIntersections);
|
||||
Collection<Point> tmpIntersections = IntersectionComputer.getInstance().compute(setOfLines, interval.getLower(), interval.getUpper());
|
||||
boolean found = false;
|
||||
for (Point tmpIntersection : tmpIntersections) {
|
||||
if (tmpIntersection.getX() > interval.getLower()
|
||||
|
@ -154,7 +147,7 @@ public class LeastMedianOfSquaresEstimator implements Algorithm, Flow.Publisher<
|
|||
}
|
||||
}
|
||||
end = System.currentTimeMillis();
|
||||
Logging.logInfo("Zeit: " + ((end - start) / 1000));
|
||||
Logging.logInfo("=== E N D - L M S === " + ((end - start) / 1000));
|
||||
|
||||
return pepareResult();
|
||||
}
|
||||
|
@ -166,7 +159,7 @@ public class LeastMedianOfSquaresEstimator implements Algorithm, Flow.Publisher<
|
|||
* @return Anzahl der Schnittpunkte
|
||||
*/
|
||||
public int countInversions(Interval interval) {
|
||||
return invCounter.run(set, interval);
|
||||
return IntersectionComputer.getInstance().compute(setOfLines, interval.getLower(), interval.getUpper()).size();
|
||||
}
|
||||
|
||||
|
||||
|
@ -183,7 +176,8 @@ public class LeastMedianOfSquaresEstimator implements Algorithm, Flow.Publisher<
|
|||
|
||||
//initialisiere die x-Queue mit den 2D Punkten und sortiere nach x-Lexikographischer Ordnung
|
||||
List<Point> xQueue = new ArrayList<>();
|
||||
for (Point point : intersections) {
|
||||
Collection<Point> points = IntersectionComputer.getInstance().compute(setOfLines, interval.getLower(), interval.getUpper());
|
||||
for (Point point : points) {
|
||||
if (point.getX() >= interval.getLower() && point.getX() < interval.getUpper()) {
|
||||
xQueue.add(point);
|
||||
}
|
||||
|
@ -271,7 +265,7 @@ public class LeastMedianOfSquaresEstimator implements Algorithm, Flow.Publisher<
|
|||
|
||||
//y koordinaten der Schnittpunkte
|
||||
List<Line> lines = new ArrayList<>();
|
||||
for (Line p : set) {
|
||||
for (Line p : setOfLines) {
|
||||
lines.add(
|
||||
new Line(pslab.getLower(), pslab.getUpper(), ((pslab.getLower() * p.getM()) + p.getB()),
|
||||
((pslab.getUpper() * p.getM()) + p.getB())));
|
||||
|
@ -333,7 +327,7 @@ public class LeastMedianOfSquaresEstimator implements Algorithm, Flow.Publisher<
|
|||
|
||||
List<Double> ret = new ArrayList<>();
|
||||
|
||||
for (Line p : set) {
|
||||
for (Line p : setOfLines) {
|
||||
ret.add((p.getM() * u) + p.getB());
|
||||
}
|
||||
|
||||
|
@ -354,7 +348,7 @@ public class LeastMedianOfSquaresEstimator implements Algorithm, Flow.Publisher<
|
|||
|
||||
//y Koordinaten für das kMinus brecalet
|
||||
List<Double> intersections = new LinkedList<>();
|
||||
for (Line line : set) {
|
||||
for (Line line : setOfLines) {
|
||||
intersections.add((px.getX() * line.getM()) + line.getB());
|
||||
}
|
||||
if (intersections.size() >= kMinusValue) {
|
||||
|
|
|
@ -2,20 +2,15 @@ package de.wwwu.awolf.presenter.algorithms.advanced;
|
|||
|
||||
import de.wwwu.awolf.model.Interval;
|
||||
import de.wwwu.awolf.model.Line;
|
||||
import de.wwwu.awolf.model.Point;
|
||||
import de.wwwu.awolf.model.communication.AlgorithmData;
|
||||
import de.wwwu.awolf.model.communication.Data;
|
||||
import de.wwwu.awolf.model.communication.SubscriberType;
|
||||
import de.wwwu.awolf.presenter.Presenter;
|
||||
import de.wwwu.awolf.presenter.algorithms.Algorithm;
|
||||
import de.wwwu.awolf.presenter.util.FastElementSelector;
|
||||
import de.wwwu.awolf.presenter.util.IntersectionCounter;
|
||||
import de.wwwu.awolf.presenter.util.Logging;
|
||||
import de.wwwu.awolf.presenter.util.RandomSampler;
|
||||
import de.wwwu.awolf.presenter.util.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Flow;
|
||||
|
||||
|
||||
|
@ -29,20 +24,19 @@ import java.util.concurrent.Flow;
|
|||
public class RepeatedMedianEstimator implements Algorithm, Flow.Publisher<Data> {
|
||||
|
||||
private Presenter presenter;
|
||||
private List<Line> set;
|
||||
private Map<Line, Double> medianIntersections = new HashMap<>();
|
||||
private Map<Line, ArrayList<Double>> intersectionAbscissas = new HashMap<>();
|
||||
private List<Line> setOfLines;
|
||||
private Interval interval;
|
||||
private Interval original;
|
||||
|
||||
//in der Literatur als L_i, C_i, und R_i bekannt
|
||||
private List<Double> countLeftSlab;
|
||||
private List<Double> countCenterSlab;
|
||||
private List<Double> countRightSlab;
|
||||
private int countLeftSlab;
|
||||
private int countCenterSlab;
|
||||
private int countRightSlab;
|
||||
|
||||
//die Mengen L,C und R
|
||||
private List<Line> linesInLeftSlab;
|
||||
private List<Line> linesInCenterSlab;
|
||||
private List<Line> linesInRightSlab;
|
||||
//in der Literatur als L_i, C_i, und R_i bekannt
|
||||
private Set<Point> intersectionsInLeftSlab;
|
||||
private Set<Point> intersectionsInCenterSlab;
|
||||
private Set<Point> intersectionsInRightSlab;
|
||||
|
||||
private double r;
|
||||
private int n;
|
||||
|
@ -51,9 +45,6 @@ public class RepeatedMedianEstimator implements Algorithm, Flow.Publisher<Data>
|
|||
private double kHigh;
|
||||
private double beta;
|
||||
|
||||
private double thetaLow;
|
||||
private double thetaHigh;
|
||||
|
||||
private double slope;
|
||||
private double yInterception;
|
||||
private Flow.Subscriber<? super AlgorithmData> subscriber;
|
||||
|
@ -61,39 +52,41 @@ public class RepeatedMedianEstimator implements Algorithm, Flow.Publisher<Data>
|
|||
/**
|
||||
* Konstruktor
|
||||
*
|
||||
* @param set Liste der Geraden
|
||||
* @param presenter Presenter (Beobachter)
|
||||
* @param setOfLines Liste der Geraden
|
||||
* @param presenter Presenter (Beobachter)
|
||||
*/
|
||||
public RepeatedMedianEstimator(List<Line> set, Presenter presenter) {
|
||||
this.set = set;
|
||||
public RepeatedMedianEstimator(List<Line> setOfLines, Presenter presenter) {
|
||||
this.setOfLines = setOfLines;
|
||||
this.presenter = presenter;
|
||||
interval = new Interval(-10000, 10000);
|
||||
n = set.size();
|
||||
original = new Interval(-10000,10000);
|
||||
n = setOfLines.size();
|
||||
beta = 0.5;
|
||||
countLeftSlab = new ArrayList<>();
|
||||
countCenterSlab = new ArrayList<>();
|
||||
countRightSlab = new ArrayList<>();
|
||||
|
||||
intersectionsInLeftSlab = new HashSet<>();
|
||||
intersectionsInCenterSlab = new HashSet<>();
|
||||
intersectionsInRightSlab = new HashSet<>();
|
||||
|
||||
intersectionsInLeftSlab.add(new Point(0d,0d));
|
||||
intersectionsInCenterSlab.add(new Point(0d,0d));
|
||||
intersectionsInCenterSlab.add(new Point(0d,0d));
|
||||
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
countLeftSlab.add(0d);
|
||||
countRightSlab.add(0d);
|
||||
countCenterSlab.add(n - 1.0);
|
||||
intersectionAbscissas.put(set.get(i), new ArrayList<>());
|
||||
countLeftSlab = 0;
|
||||
countRightSlab = 0;
|
||||
countCenterSlab = setOfLines.size();
|
||||
}
|
||||
|
||||
linesInLeftSlab = new ArrayList<>();
|
||||
linesInCenterSlab = new ArrayList<>(set);
|
||||
linesInRightSlab = new ArrayList<>();
|
||||
subscribe(presenter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Konstruktor
|
||||
*
|
||||
* @param set Liste der Geraden
|
||||
* @param setOfLines Liste der Geraden
|
||||
*/
|
||||
public RepeatedMedianEstimator(List<Line> set) {
|
||||
this(set, null);
|
||||
public RepeatedMedianEstimator(List<Line> setOfLines) {
|
||||
this(setOfLines, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -110,42 +103,46 @@ public class RepeatedMedianEstimator implements Algorithm, Flow.Publisher<Data>
|
|||
long start;
|
||||
long end;
|
||||
start = System.currentTimeMillis();
|
||||
double thetaLow = 0;
|
||||
double thetaHigh = 0;
|
||||
|
||||
while (linesInCenterSlab.size() != 1) {
|
||||
n = linesInCenterSlab.size();
|
||||
while (countCenterSlab > 1) {
|
||||
n = countCenterSlab;
|
||||
r = Math.ceil(Math.pow(n, beta));
|
||||
List<Line> lines = RandomSampler.run(linesInCenterSlab, r, linesInCenterSlab.size());
|
||||
List<Line> lines = RandomSampler.run(setOfLines, r);
|
||||
|
||||
//Für jede Gerade aus der Stichprobe wird der Schnittpunkt mit der medianen
|
||||
//x-Koordinate bestimmt
|
||||
List<Double> medianIntersectionAbscissas = new ArrayList<>();
|
||||
for (Line l : lines) {
|
||||
Double abscissa = estimateMedianIntersectionAbscissas(l);
|
||||
medianIntersections.put(l, abscissa);
|
||||
medianIntersectionAbscissas.add(abscissa);
|
||||
final double lowerBound = thetaLow;
|
||||
final double upperBound = thetaHigh;
|
||||
if (!lines.isEmpty()) {
|
||||
lines.forEach(line -> medianIntersectionAbscissas.add(estimateMedianIntersectionAbscissas(lines, line)));
|
||||
}
|
||||
|
||||
//Rang vom RM-Wert in C
|
||||
k = Math.max(1, Math.min(set.size(), (Math.ceil(n * 0.5) - linesInLeftSlab.size())));
|
||||
k = Math.max(1, Math.min(n, (Math.ceil(n * 0.5) - countLeftSlab)));
|
||||
|
||||
//berechne k_lo und k_hi
|
||||
computeSlabBorders();
|
||||
|
||||
//Berechne die Elemente mit dem Rang Theta_lo und Theta_hi
|
||||
Collections.sort(medianIntersectionAbscissas);
|
||||
thetaLow = FastElementSelector.randomizedSelect(medianIntersectionAbscissas, kLow);
|
||||
thetaHigh = FastElementSelector.randomizedSelect(medianIntersectionAbscissas, kHigh);
|
||||
|
||||
//Für jede Gerade in C wird die Anzahl der Schnittpunkte die im Intervall liegen hochgezählt
|
||||
countNumberOfIntersectionsAbscissas();
|
||||
countNumberOfIntersectionsAbscissas(thetaLow, thetaHigh);
|
||||
|
||||
//verkleinere das Intervall
|
||||
contractIntervals();
|
||||
contractIntervals(thetaLow, thetaHigh);
|
||||
|
||||
}
|
||||
end = System.currentTimeMillis();
|
||||
Logging.logInfo("Zeit: " + ((end - start) / 1000));
|
||||
|
||||
return pepareResult();
|
||||
end = System.currentTimeMillis();
|
||||
Logging.logInfo("=== E N D - R M === " + ((end - start) / 1000));
|
||||
|
||||
return pepareResult(thetaLow, thetaHigh);
|
||||
}
|
||||
|
||||
|
||||
|
@ -155,18 +152,20 @@ public class RepeatedMedianEstimator implements Algorithm, Flow.Publisher<Data>
|
|||
* @param sampledLine Stichprobe von Geraden
|
||||
* @return mediane x-Koordinate über den Schnittpunkten
|
||||
*/
|
||||
public Double estimateMedianIntersectionAbscissas(Line sampledLine) {
|
||||
public Double estimateMedianIntersectionAbscissas(List<Line> lines, Line sampledLine) {
|
||||
|
||||
int index = Integer.parseInt(sampledLine.getId());
|
||||
IntersectionCounter intersectionCounter = new IntersectionCounter();
|
||||
List<Double> intersections = intersectionCounter.calculateIntersectionAbscissas(linesInCenterSlab, sampledLine);
|
||||
List<Double> intersections = intersectionCounter.calculateIntersectionAbscissas(lines, sampledLine, original.getLower(), original.getUpper());
|
||||
List<Double> left = intersectionCounter.calculateIntersectionAbscissas(lines, sampledLine, original.getLower(), interval.getLower());
|
||||
List<Double> center = intersectionCounter.calculateIntersectionAbscissas(lines, sampledLine, interval.getLower(), interval.getUpper());
|
||||
|
||||
double ki = Math.ceil((n - 1) * 0.5) - FastElementSelector.randomizedSelect(countLeftSlab, index);
|
||||
double i = (Math.ceil((Math.sqrt(n) * ki) / FastElementSelector.randomizedSelect(countCenterSlab, index)));
|
||||
|
||||
double ki = Math.ceil((n - 1) * 0.5) - left.size();
|
||||
double i = (Math.ceil((Math.sqrt(n) * ki) / center.size()));
|
||||
int accessIndex;
|
||||
if (i < 0)
|
||||
accessIndex = 0;
|
||||
else if (i >= intersections.size())
|
||||
else if (i >= intersections.size() && !intersections.isEmpty())
|
||||
accessIndex = intersections.size() - 1;
|
||||
else
|
||||
accessIndex = (int) i;
|
||||
|
@ -178,9 +177,9 @@ public class RepeatedMedianEstimator implements Algorithm, Flow.Publisher<Data>
|
|||
* Berechnet die potenziell neuen Intervallgrenzen.
|
||||
*/
|
||||
public void computeSlabBorders() {
|
||||
kLow = Math.max(1, Math.floor(((r * k) / (linesInCenterSlab.size()))
|
||||
kLow = Math.max(1, Math.floor(((r * k) / (countCenterSlab))
|
||||
- ((3 * Math.sqrt(r)) * (0.5))));
|
||||
kHigh = Math.min(r, Math.floor(((r * k) / (linesInCenterSlab.size()))
|
||||
kHigh = Math.min(r, Math.floor(((r * k) / (countCenterSlab))
|
||||
+ ((3 * Math.sqrt(r)) * (0.5))));
|
||||
}
|
||||
|
||||
|
@ -189,87 +188,56 @@ public class RepeatedMedianEstimator implements Algorithm, Flow.Publisher<Data>
|
|||
* 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() {
|
||||
for (Line line : linesInCenterSlab) {
|
||||
List<Double> intersections = intersectionAbscissas.get(line);
|
||||
int index = Integer.parseInt(line.getId());
|
||||
int left = 0;
|
||||
int center = 0;
|
||||
int right = 0;
|
||||
public void countNumberOfIntersectionsAbscissas(final double lower, final double upper) {
|
||||
IntersectionComputer instance = IntersectionComputer.getInstance();
|
||||
|
||||
for (Double intersection : intersections) {
|
||||
if (intersection <= thetaLow) {
|
||||
left++;
|
||||
} else if (intersection > thetaLow && intersection <= thetaHigh) {
|
||||
center++;
|
||||
} else if (intersection > thetaHigh) {
|
||||
right++;
|
||||
}
|
||||
}
|
||||
|
||||
countLeftSlab.set(index, (double) left);
|
||||
countCenterSlab.set(index, (double) center);
|
||||
countRightSlab.set(index, (double) right);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO bad idea!!!
|
||||
* Verkleinert das aktuelle Intervall. Eines der drei Bereiche wird als neues Intervall gewählt.
|
||||
* Auf diesem Intervall werden dann in der nächsten Iteration wieder drei Bereiche bestimmt.
|
||||
*/
|
||||
public void contractIntervals() {
|
||||
for (int i = 0; i < linesInCenterSlab.size(); i++) {
|
||||
public void contractIntervals(final double lower, final double upper) {
|
||||
double max = Math.max(countLeftSlab, Math.max(countCenterSlab, countRightSlab));
|
||||
|
||||
double left = countLeftSlab.get(i);
|
||||
double center = countCenterSlab.get(i);
|
||||
double right = countRightSlab.get(i);
|
||||
|
||||
double max = Math.max(left, Math.max(center, right));
|
||||
|
||||
if (left == max) {
|
||||
linesInLeftSlab.add(linesInCenterSlab.get(i));
|
||||
linesInCenterSlab.remove(i);
|
||||
} else if (right == max) {
|
||||
linesInRightSlab.add(linesInCenterSlab.get(i));
|
||||
linesInCenterSlab.remove(i);
|
||||
}
|
||||
}
|
||||
boolean newIntervalIsC = countLeftSlab < Math.ceil(n * 0.5) && Math.ceil(n * 0.5) <= countLeftSlab + countCenterSlab;
|
||||
boolean newIntervalIsL = Math.ceil(n * 0.5) <= countLeftSlab;
|
||||
boolean newIntervalIsR = countLeftSlab + countCenterSlab < Math.ceil(n * 0.5) && Math.ceil(n * 0.5) <= (countLeftSlab + countCenterSlab + countRightSlab);
|
||||
|
||||
//wähle C als C
|
||||
if (linesInLeftSlab.size() < Math.ceil(n * 0.5) && Math.ceil(n * 0.5) <= linesInLeftSlab.size() + linesInCenterSlab.size()) {
|
||||
interval.setLower(thetaLow + 0.1);
|
||||
interval.setUpper(thetaHigh);
|
||||
}
|
||||
// wähle L als C
|
||||
else if (Math.ceil(n * 0.5) <= linesInLeftSlab.size()) {
|
||||
interval.setUpper(thetaLow);
|
||||
}
|
||||
//wähle R als C
|
||||
else if (linesInLeftSlab.size() + linesInCenterSlab.size() < Math.ceil(n * 0.5) && Math.ceil(n * 0.5) <= (linesInLeftSlab.size() + linesInCenterSlab.size() + linesInRightSlab.size())) {
|
||||
interval.setLower(thetaHigh - 0.1);
|
||||
if (newIntervalIsC) {
|
||||
interval.setLower(lower);
|
||||
interval.setUpper(upper);
|
||||
} else if (newIntervalIsL) {
|
||||
interval.setUpper(lower);
|
||||
} else if (newIntervalIsR) {
|
||||
interval.setLower(upper);
|
||||
}
|
||||
}
|
||||
|
||||
private Line pepareResult() {
|
||||
if (this.subscriber != null) {
|
||||
double m = thetaLow;
|
||||
double b = (-1) * (
|
||||
(linesInCenterSlab.get(0).getM() * (thetaLow)) + linesInCenterSlab.get(0)
|
||||
.getB());
|
||||
private Line pepareResult(final double thetaLow, final double thetaHigh) {
|
||||
slope = thetaLow;
|
||||
List<Double> potentialYInterceptions = new ArrayList<>();
|
||||
setOfLines.forEach(line -> {
|
||||
potentialYInterceptions.add(line.getB() - (slope * line.getM()));
|
||||
});
|
||||
|
||||
slope = m;
|
||||
yInterception = b;
|
||||
yInterception = FastElementSelector.randomizedSelect(potentialYInterceptions, Math.floor(potentialYInterceptions.size() * 0.5 ));
|
||||
|
||||
if (this.subscriber != null) {
|
||||
AlgorithmData data = new AlgorithmData();
|
||||
data.setType(SubscriberType.RM);
|
||||
data.setLineData(new Line(m, b));
|
||||
data.setLineData(new Line(getSlope(), getyInterception()));
|
||||
this.subscriber.onNext(data);
|
||||
} else {
|
||||
double m = thetaLow;
|
||||
double b = (-1) * ((linesInCenterSlab.get(0).getM() * (thetaLow)) + linesInCenterSlab.get(0).getB());
|
||||
slope = m;
|
||||
yInterception = b;
|
||||
}
|
||||
|
||||
return new Line(getSlope(), getyInterception());
|
||||
|
@ -341,21 +309,21 @@ public class RepeatedMedianEstimator implements Algorithm, Flow.Publisher<Data>
|
|||
/**
|
||||
* @return verteilung der Punkte
|
||||
*/
|
||||
public List<Double> getCountLeftSlab() {
|
||||
public int getCountLeftSlab() {
|
||||
return countLeftSlab;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return verteilung der Punkte
|
||||
*/
|
||||
public List<Double> getCountCenterSlab() {
|
||||
public int getCountCenterSlab() {
|
||||
return countCenterSlab;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return verteilung der Punkte
|
||||
*/
|
||||
public List<Double> getCountRightSlab() {
|
||||
public int getCountRightSlab() {
|
||||
return countRightSlab;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,15 +8,9 @@ import de.wwwu.awolf.model.communication.Data;
|
|||
import de.wwwu.awolf.model.communication.SubscriberType;
|
||||
import de.wwwu.awolf.presenter.Presenter;
|
||||
import de.wwwu.awolf.presenter.algorithms.Algorithm;
|
||||
import de.wwwu.awolf.presenter.util.BinomialCoeffizient;
|
||||
import de.wwwu.awolf.presenter.util.FastElementSelector;
|
||||
import de.wwwu.awolf.presenter.util.IntersectionCounter;
|
||||
import de.wwwu.awolf.presenter.util.RandomSampler;
|
||||
import de.wwwu.awolf.presenter.util.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Flow;
|
||||
|
||||
/**
|
||||
|
@ -32,12 +26,8 @@ public class TheilSenEstimator implements Algorithm, Flow.Publisher<Data> {
|
|||
private final double NEGATIV_INF = -99999.0;
|
||||
private final double EPSILON = 0.00001;
|
||||
private List<Line> setOfLines;
|
||||
private List<Point> setOfIntersections;
|
||||
private List<Point> intervalIntersections;
|
||||
private List<Double> sampledIntersections;
|
||||
//wird benötigt um den y Achsenabschnitt zu Berechnen
|
||||
private List<Double> yCoordinates;
|
||||
private List<Double> xCoordinates;
|
||||
private List<Point> sampledIntersections;
|
||||
//Hilfsvariablen (siehe original Paper)
|
||||
private double j;
|
||||
private int jA;
|
||||
|
@ -53,24 +43,21 @@ public class TheilSenEstimator implements Algorithm, Flow.Publisher<Data> {
|
|||
private double slope;
|
||||
private double yInterception;
|
||||
private Flow.Subscriber<? super AlgorithmData> subscriber;
|
||||
private Collection<Point> setOfIntersections;
|
||||
|
||||
/**
|
||||
* Konstruktor
|
||||
*
|
||||
* @param setOfLines Liste der Geraden
|
||||
* @param setOfIntersections Liste der Schnittpunkte
|
||||
* @param presenter Presenter (Beobachter)
|
||||
*/
|
||||
public TheilSenEstimator(List<Line> setOfLines, List<Point> setOfIntersections, Presenter presenter) {
|
||||
public TheilSenEstimator(List<Line> setOfLines, Presenter presenter) {
|
||||
|
||||
this.setOfLines = new ArrayList<>(setOfLines);
|
||||
this.setOfIntersections = new ArrayList<>(setOfIntersections);
|
||||
this.intervalIntersections = new ArrayList<>(setOfIntersections);
|
||||
this.setOfIntersections = new HashSet<>();
|
||||
|
||||
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)) - 1;
|
||||
this.k = (int) (N / 2);
|
||||
|
@ -83,10 +70,9 @@ public class TheilSenEstimator implements Algorithm, Flow.Publisher<Data> {
|
|||
* Konstruktor
|
||||
*
|
||||
* @param setOfLines Liste der Geraden
|
||||
* @param setOfIntersections Liste der Schnittpunkte
|
||||
*/
|
||||
public TheilSenEstimator(List<Line> setOfLines, List<Point> setOfIntersections) {
|
||||
this(setOfLines, setOfIntersections, null);
|
||||
public TheilSenEstimator(List<Line> setOfLines) {
|
||||
this(setOfLines, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,7 +91,7 @@ public class TheilSenEstimator implements Algorithm, Flow.Publisher<Data> {
|
|||
break;
|
||||
} else {
|
||||
//Anzahl der Schnittpunkte im Intervall [-Inf, a)
|
||||
int numberOfIntersections = getOpenIntervalSize(NEGATIV_INF, interval.getLower(), setOfIntersections);
|
||||
int numberOfIntersections = getOpenIntervalSize(NEGATIV_INF, interval.getLower());
|
||||
|
||||
//Randomized Interpolating Search
|
||||
j = (r / N) * (double) (k - numberOfIntersections);
|
||||
|
@ -119,20 +105,27 @@ public class TheilSenEstimator implements Algorithm, Flow.Publisher<Data> {
|
|||
do {
|
||||
//zufällige Stichprobe
|
||||
sampledIntersections = RandomSampler.run(intervalIntersections, r);
|
||||
aVariant = FastElementSelector.randomizedSelect(sampledIntersections, jA);
|
||||
bVariant = FastElementSelector.randomizedSelect(sampledIntersections, jB);
|
||||
|
||||
aVariant = FastElementSelector.randomizedSelect(getIntersectionAbscissas(sampledIntersections), jA);
|
||||
bVariant = FastElementSelector.randomizedSelect(getIntersectionAbscissas(sampledIntersections), jB);
|
||||
} while (!checkCondition());
|
||||
|
||||
interval.setLower(aVariant);
|
||||
interval.setUpper(bVariant);
|
||||
intervalIntersections = getOpenIntervalElements(interval.getLower(), interval.getUpper());
|
||||
N = getOpenIntervalSize(interval.getLower(), interval.getUpper(), intervalIntersections);
|
||||
N = getOpenIntervalSize(interval.getLower(), interval.getUpper());
|
||||
}
|
||||
}
|
||||
|
||||
return pepareResult();
|
||||
}
|
||||
|
||||
private List<Double> getIntersectionAbscissas(List<Point> interections) {
|
||||
List<Double> abscissas = new ArrayList<>();
|
||||
interections.forEach(e -> abscissas.add(e.getX()));
|
||||
return abscissas;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Diese Funktion überprüft ob die Bedingung für das Interval erfüllt ist. Dabei muss der k-te
|
||||
|
@ -183,15 +176,10 @@ public class TheilSenEstimator implements Algorithm, Flow.Publisher<Data> {
|
|||
* @param b obere Grenze des Intrvals
|
||||
* @return Anzahl der Schnittpunkte im Interval (a,b)
|
||||
*/
|
||||
public int getOpenIntervalSize(double a, double b, List<Point> set) {
|
||||
int counter = 0;
|
||||
for (int i = 0; i < set.size(); i++) {
|
||||
Point x = set.get(i);
|
||||
if (x.getX() > a && x.getX() < b) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
return counter;
|
||||
public int getOpenIntervalSize(double a, double b) {
|
||||
Collection<Point> intersections = IntersectionComputer.getInstance().compute(setOfLines, a, b);
|
||||
setOfIntersections.addAll(intersections);
|
||||
return intersections.size();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -227,17 +215,19 @@ public class TheilSenEstimator implements Algorithm, Flow.Publisher<Data> {
|
|||
resultAbscissas.add(p.getX());
|
||||
}
|
||||
|
||||
List<Double> yCoords = new ArrayList<>();
|
||||
|
||||
for (Point p : setOfIntersections) {
|
||||
yCoordinates.add(p.getY());
|
||||
yCoords.add(p.getY());
|
||||
}
|
||||
|
||||
double pseudoIndex = getOpenIntervalSize(NEGATIV_INF, interval.getLower(), setOfIntersections) * 1.0;
|
||||
double pseudoIndex = getOpenIntervalSize(NEGATIV_INF, interval.getLower()) * 1.0;
|
||||
m = FastElementSelector.randomizedSelect(resultAbscissas, k - pseudoIndex);
|
||||
|
||||
Set<Double> unique = new LinkedHashSet<>(yCoordinates);
|
||||
yCoordinates.clear();
|
||||
yCoordinates.addAll(unique);
|
||||
b = FastElementSelector.randomizedSelect(yCoordinates, yCoordinates.size() * 0.5) * -1;
|
||||
Set<Double> unique = new LinkedHashSet<>(yCoords);
|
||||
yCoords.clear();
|
||||
yCoords.addAll(unique);
|
||||
b = FastElementSelector.randomizedSelect(yCoords, yCoords.size() * 0.5) * -1;
|
||||
slope = m;
|
||||
yInterception = b;
|
||||
|
||||
|
|
|
@ -16,13 +16,12 @@ import java.util.Map;
|
|||
*/
|
||||
public class DatasetGenerator {
|
||||
|
||||
private static SecureRandom random;
|
||||
|
||||
/**
|
||||
* Konstruktor
|
||||
*/
|
||||
private DatasetGenerator() {
|
||||
random = new SecureRandom();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,6 +33,7 @@ public class DatasetGenerator {
|
|||
* @return Liste des Geraden
|
||||
*/
|
||||
public static List<Line> generateDataLines(List<Line> lines, int n) {
|
||||
|
||||
double m = 5d;
|
||||
double b = 0d;
|
||||
|
||||
|
@ -42,6 +42,7 @@ public class DatasetGenerator {
|
|||
|
||||
//speichere die Koordinaten in einer HashMap, damit keine Punkte
|
||||
//entstehen deren x-Koordinate zu sehr beieinander liegt.
|
||||
SecureRandom random = new SecureRandom();
|
||||
while (size < n) {
|
||||
double y = random.nextGaussian();
|
||||
double signal = m * y + b;
|
||||
|
@ -54,8 +55,8 @@ public class DatasetGenerator {
|
|||
}
|
||||
|
||||
int idx = lines.size();
|
||||
for (Double d : points.keySet()) {
|
||||
Line line = new Line(d, points.get(d));
|
||||
for (Map.Entry<Double, Double> d : points.entrySet()) {
|
||||
Line line = new Line(d.getKey(), d.getValue());
|
||||
line.setId(idx + "");
|
||||
lines.add(line);
|
||||
idx++;
|
||||
|
|
|
@ -2,7 +2,6 @@ package de.wwwu.awolf.presenter.data.io;
|
|||
|
||||
import com.opencsv.CSVWriter;
|
||||
import de.wwwu.awolf.model.Line;
|
||||
import de.wwwu.awolf.model.communication.Data;
|
||||
import de.wwwu.awolf.presenter.util.Logging;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -43,7 +42,7 @@ public class DataExporter implements Runnable {
|
|||
CSVWriter writer = null;
|
||||
|
||||
try {
|
||||
writer = new CSVWriter(new FileWriter(file), ',');
|
||||
writer = new CSVWriter(new FileWriter(file));
|
||||
String[] entries = new String[3];
|
||||
for (Line line : lines) {
|
||||
entries[0] = line.getId();
|
||||
|
|
|
@ -24,15 +24,15 @@ public class AlgorithmComparison {
|
|||
private final Map<Algorithm.Type, Algorithm> algorithmMap;
|
||||
private final CompletionService<Line> completionService;
|
||||
|
||||
public AlgorithmComparison(List<Algorithm.Type> types, List<Line> lines, List<Point> nodes) {
|
||||
public AlgorithmComparison(List<Algorithm.Type> types, List<Line> lines) {
|
||||
this.types = types;
|
||||
this.executorService = Executors.newFixedThreadPool(3);
|
||||
completionService = new ExecutorCompletionService<>(this.executorService);
|
||||
|
||||
algorithmMap = new EnumMap<>(Algorithm.Type.class);
|
||||
algorithmMap.put(Algorithm.Type.LMS, new LeastMedianOfSquaresEstimator(lines, nodes));
|
||||
algorithmMap.put(Algorithm.Type.LMS, new LeastMedianOfSquaresEstimator(lines));
|
||||
algorithmMap.put(Algorithm.Type.RM, new RepeatedMedianEstimator(lines));
|
||||
algorithmMap.put(Algorithm.Type.TS, new TheilSenEstimator(lines, nodes));
|
||||
algorithmMap.put(Algorithm.Type.TS, new TheilSenEstimator(lines));
|
||||
algorithmMap.put(Algorithm.Type.NAIV_LMS, new NaivLeastMedianOfSquaresEstimator(lines));
|
||||
algorithmMap.put(Algorithm.Type.NAIV_RM, new NaivRepeatedMedianEstimator(lines));
|
||||
algorithmMap.put(Algorithm.Type.NAIV_TS, new NaivTheilSenEstimator(lines));
|
||||
|
|
|
@ -67,9 +67,6 @@ public class EvaluateAlgorithms implements Runnable, Flow.Publisher<Data> {
|
|||
this.type = type;
|
||||
this.iterations = n;
|
||||
this.alg = alg;
|
||||
|
||||
IntersectionComputer computer = new IntersectionComputer(arrangement.getLines());
|
||||
arrangement.setNodes(computer.compute());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,14 +90,11 @@ public class EvaluateAlgorithms implements Runnable, Flow.Publisher<Data> {
|
|||
|
||||
this.type = type;
|
||||
this.alg = alg;
|
||||
|
||||
IntersectionComputer computer = new IntersectionComputer(arrangement.getLines());
|
||||
arrangement.setNodes(computer.compute());
|
||||
}
|
||||
|
||||
private Map<Algorithm.Type, Map<String, String>> benchmarkSameEstimator(final Algorithm.Type advanced, final Algorithm.Type naiv) {
|
||||
Logging.logInfo("AlgorithmComparison with Types: " + advanced.name() + ", " + naiv.name());
|
||||
AlgorithmComparison comparison = new AlgorithmComparison(Arrays.asList(naiv, advanced), Collections.unmodifiableList(arrangement.getLines()), Collections.unmodifiableList(arrangement.getNodes()));
|
||||
AlgorithmComparison comparison = new AlgorithmComparison(Arrays.asList(naiv, advanced), Collections.unmodifiableList(arrangement.getLines()));
|
||||
ComparisonResult comparisonResult = comparison.compare();
|
||||
|
||||
|
||||
|
@ -115,11 +109,10 @@ public class EvaluateAlgorithms implements Runnable, Flow.Publisher<Data> {
|
|||
|
||||
private Map<Algorithm.Type, Map<String, String>> benchmarkDifferentEstimators(List<Algorithm.Type> types) {
|
||||
Logging.logInfo("AlgorithmComparison with Types: " + types);
|
||||
AlgorithmComparison comparison = new AlgorithmComparison(types, Collections.unmodifiableList(arrangement.getLines()), Collections.unmodifiableList(arrangement.getNodes()));
|
||||
AlgorithmComparison comparison = new AlgorithmComparison(types, Collections.unmodifiableList(arrangement.getLines()));
|
||||
ComparisonResult comparisonResult = comparison.compare();
|
||||
|
||||
Map<String, String> multipleResults = new HashMap<>();
|
||||
types.forEach(type -> this.resultMapping.put(type, getPercentigeErrorBasedMeasure(arrangement.getLines(), comparisonResult, type)));
|
||||
types.forEach(typeEntry -> this.resultMapping.put(typeEntry, getPercentigeErrorBasedMeasure(arrangement.getLines(), comparisonResult, typeEntry)));
|
||||
Logging.logInfo("finished with execution of the algorithms.");
|
||||
return this.resultMapping;
|
||||
}
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
package de.wwwu.awolf.presenter.evaluation.measures;
|
||||
|
||||
import de.wwwu.awolf.model.Line;
|
||||
import de.wwwu.awolf.model.communication.Data;
|
||||
import de.wwwu.awolf.model.communication.SubscriberType;
|
||||
import de.wwwu.awolf.model.communication.TypeData;
|
||||
import de.wwwu.awolf.presenter.Presenter;
|
||||
import de.wwwu.awolf.presenter.util.Logging;
|
||||
import org.opencv.core.*;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferByte;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Flow;
|
||||
|
||||
/**
|
||||
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
|
||||
*
|
||||
* @Author: Armin Wolf
|
||||
* @Email: a_wolf28@uni-muenster.de
|
||||
* @Date: 17.09.2017.
|
||||
*/
|
||||
public class PictureProcessor implements Flow.Publisher<Data> {
|
||||
|
||||
private Mat image;
|
||||
private Mat contour;
|
||||
private Presenter presenter;
|
||||
private File file;
|
||||
private ArrayList<MatOfPoint> contours;
|
||||
private double contourLength;
|
||||
private Flow.Subscriber<? super Data> subscriber;
|
||||
|
||||
/**
|
||||
* Konstruktor
|
||||
*
|
||||
* @param presenter Presenter
|
||||
* @param file Bilddatei
|
||||
*/
|
||||
public PictureProcessor(Presenter presenter, File file) {
|
||||
this.file = file;
|
||||
this.presenter = presenter;
|
||||
subscribe(presenter);
|
||||
}
|
||||
|
||||
/**
|
||||
* startet den Import des Bild und die Vorverarbeitung
|
||||
*/
|
||||
public void run() {
|
||||
image = Imgcodecs.imread(file.getAbsolutePath());
|
||||
contour = process(image);
|
||||
contourLength = image.width() * 0.3;
|
||||
createInputData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vorverarbeitung des Eingabebilds. Dabei wird auf verschiedene OpenCV Methoden zurückgegriffen.
|
||||
*
|
||||
* @param image Eingabebild
|
||||
* @return Matrix representation des Bilds
|
||||
*/
|
||||
private Mat process(Mat image) {
|
||||
Mat threshold = new Mat(image.width(), image.height(), CvType.CV_8UC1);
|
||||
Mat source = new Mat(image.width(), image.height(), CvType.CV_8UC1);
|
||||
Imgcodecs.imwrite(file.getAbsolutePath().substring(0, file.getAbsolutePath().lastIndexOf(".")) + "-orig.png", image);
|
||||
Imgproc.cvtColor(image, source, Imgproc.COLOR_BGR2GRAY);
|
||||
Imgcodecs.imwrite(file.getAbsolutePath().substring(0, file.getAbsolutePath().lastIndexOf(".")) + "-Greyscale.png", source);
|
||||
Imgproc.GaussianBlur(source, threshold, new Size(3, 3), 0, 0);
|
||||
Imgproc.Canny(threshold, source, 300, 600, 5, true);
|
||||
Imgcodecs.imwrite(file.getAbsolutePath().substring(0, file.getAbsolutePath().lastIndexOf(".")) + "-Canny.png", source);
|
||||
//Konturen berechnen und filtern
|
||||
contours = new ArrayList<>();
|
||||
Imgproc.findContours(source, contours, new Mat(), Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));
|
||||
Mat viscont = new Mat(source.size(), source.type());
|
||||
double minArea = 50;
|
||||
double maxArea = 0.2 * image.cols() * image.rows();
|
||||
for (int i = 0; i < contours.size(); i++) {
|
||||
double acutualArea = Imgproc.contourArea(contours.get(i));
|
||||
if (minArea < acutualArea && maxArea > acutualArea) {
|
||||
Imgproc.drawContours(viscont, contours, i, new Scalar(255, 0, 0), 1);
|
||||
}
|
||||
}
|
||||
Imgcodecs.imwrite(file.getAbsolutePath().substring(0, file.getAbsolutePath().lastIndexOf(".")) + "-Contour.png", viscont);
|
||||
Mat resized = new Mat();
|
||||
Imgproc.resize(viscont, resized, new Size(560, 560));
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JDialog dialog = new JDialog();
|
||||
dialog.setSize(560, 560);
|
||||
JLabel label = new JLabel();
|
||||
label.setSize(560, 560);
|
||||
label.setIcon(new ImageIcon(toBufferedImage(resized)));
|
||||
dialog.add(label);
|
||||
dialog.setVisible(true);
|
||||
});
|
||||
return viscont;
|
||||
}
|
||||
|
||||
/**
|
||||
* Das Bild wird passend umgewandelt, um im nächsten Schritt visualisiert werden zu können.
|
||||
*
|
||||
* @param m Matrix representation des Bilds
|
||||
* @return visualisierbares Bild
|
||||
*/
|
||||
private BufferedImage toBufferedImage(Mat m) {
|
||||
int type = BufferedImage.TYPE_BYTE_GRAY;
|
||||
if (m.channels() > 1) {
|
||||
type = BufferedImage.TYPE_3BYTE_BGR;
|
||||
}
|
||||
int bufferSize = m.channels() * m.cols() * m.rows();
|
||||
byte[] b = new byte[bufferSize];
|
||||
m.get(0, 0, b); // get all the pixels
|
||||
BufferedImage image = new BufferedImage(m.cols(), m.rows(), type);
|
||||
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
|
||||
System.arraycopy(b, 0, targetPixels, 0, b.length);
|
||||
return image;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Das gefilerte Bild ist bereit ins Modell aufgenommen zu werden um darauf dann die Algorithmen laufen zu lassen.
|
||||
*/
|
||||
private void createInputData() {
|
||||
Thread t = new Thread(() -> {
|
||||
double minArea = 50;
|
||||
double maxArea = 0.2 * image.cols() * image.rows();
|
||||
int id = 0;
|
||||
for (int j = 0; j < contours.size(); j++) {
|
||||
Point[] p = contours.get(j).toArray();
|
||||
double acutualArea = Imgproc.contourArea(contours.get(j));
|
||||
if (minArea < acutualArea && maxArea > acutualArea) {
|
||||
for (int i = 0; i < p.length; i++) {
|
||||
Line line = new Line(-1 * (500 - p[i].x), p[i].y);
|
||||
line.setId("" + id++);
|
||||
presenter.getModel().getLines().add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TypeData data = new TypeData();
|
||||
data.setType(SubscriberType.PICTURE);
|
||||
this.subscriber.onNext(data);
|
||||
});
|
||||
t.start();
|
||||
try {
|
||||
t.join();
|
||||
} catch (InterruptedException e) {
|
||||
Logging.logError(e.getMessage(), e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(Flow.Subscriber<? super Data> subscriber) {
|
||||
this.subscriber = subscriber;
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ public class FastElementSelector {
|
|||
* @return das Element
|
||||
*/
|
||||
public static Double randomizedSelect(List<Double> a, double i) {
|
||||
|
||||
int start = 0;
|
||||
int end = a.size() - 1;
|
||||
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package de.wwwu.awolf.presenter.util;
|
||||
|
||||
import de.wwwu.awolf.model.Interval;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import de.wwwu.awolf.model.Line;
|
||||
import de.wwwu.awolf.model.Point;
|
||||
import org.jfree.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinTask;
|
||||
import java.util.concurrent.RecursiveTask;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
|
||||
|
@ -18,32 +21,68 @@ import java.util.TreeSet;
|
|||
*/
|
||||
public class IntersectionComputer {
|
||||
|
||||
private List<Line> lines;
|
||||
private Set<Point> intersections;
|
||||
|
||||
private Double xMinimum;
|
||||
private Double xMaximum;
|
||||
private Double yMinimum;
|
||||
private Double yMaximum;
|
||||
|
||||
private Thread[] worker;
|
||||
private static IntersectionComputer instance;
|
||||
|
||||
/**
|
||||
* Konstruktor
|
||||
*
|
||||
* @param lines Liste der Geraden
|
||||
*/
|
||||
public IntersectionComputer(List<Line> lines) {
|
||||
this.lines = lines;
|
||||
this.worker = new Thread[4];
|
||||
this.intersections = new TreeSet<>();
|
||||
|
||||
xMinimum = Double.MAX_VALUE;
|
||||
xMaximum = Double.MIN_VALUE;
|
||||
yMinimum = Double.MAX_VALUE;
|
||||
yMaximum = Double.MIN_VALUE;
|
||||
private IntersectionComputer() {
|
||||
}
|
||||
|
||||
private class RecursiveComputationTask extends RecursiveTask<Collection<Point>> {
|
||||
|
||||
private static final int THRESHOLD = 20;
|
||||
|
||||
private final List<Line> lines;
|
||||
private final List<Line> fullList;
|
||||
private final double lower;
|
||||
private final double upper;
|
||||
|
||||
public RecursiveComputationTask(final List<Line> fullList, final List<Line> lines, final double lower, final double upper) {
|
||||
this.lines = lines;
|
||||
this.fullList = fullList;
|
||||
this.lower = lower;
|
||||
this.upper = upper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Point> compute() {
|
||||
if (this.lines.isEmpty()){
|
||||
return Collections.emptyList();
|
||||
} else if (this.lines.size() > THRESHOLD) {
|
||||
Logging.logInfo("Bigger than threshold, split into subtask.");
|
||||
return ForkJoinTask.invokeAll(createSubTask()).stream().map(ForkJoinTask::join).flatMap(Collection::stream).collect(Collectors.toList());
|
||||
} else {
|
||||
return work(this.lines, this.lines, this.lower, this.upper);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<RecursiveComputationTask> createSubTask() {
|
||||
List<RecursiveComputationTask> dividedTasks = new ArrayList<>();
|
||||
dividedTasks.add(new RecursiveComputationTask(this.fullList, this.lines.subList(0, this.lines.size() / 2), this.lower, this.upper));
|
||||
dividedTasks.add(new RecursiveComputationTask(this.fullList, this.lines.subList(this.lines.size() / 2, this.lines.size()), this.lower, this.upper));
|
||||
return dividedTasks;
|
||||
}
|
||||
|
||||
private Collection<Point> work(List<Line> fullList, List<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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -53,141 +92,21 @@ public class IntersectionComputer {
|
|||
* @param higher obere Schranke
|
||||
* @return Liste der Schnittpunkte
|
||||
*/
|
||||
public ArrayList<Point> compute(final double lower, final double higher) {
|
||||
public Collection<Point> compute(final List<Line> lines, final double lower, final double higher) {
|
||||
Logging.logInfo("Open ForkJoinPool");
|
||||
ForkJoinPool pool = ForkJoinPool.commonPool();
|
||||
RecursiveComputationTask recursiveComputationTask = new RecursiveComputationTask(lines, lines, lower, higher);
|
||||
pool.execute(recursiveComputationTask);
|
||||
return recursiveComputationTask.join();
|
||||
}
|
||||
|
||||
if (lines.size() > 16) {
|
||||
worker[0] = new Thread(() -> {
|
||||
work(lines.subList(0, (lines.size() / 4)), lower, higher);
|
||||
});
|
||||
worker[0].start();
|
||||
worker[1] = new Thread(() -> {
|
||||
work(lines.subList((lines.size() / 4) + 1, (lines.size() / 2)), lower, higher);
|
||||
});
|
||||
worker[1].start();
|
||||
worker[2] = new Thread(() -> {
|
||||
work(lines.subList((lines.size() / 2) + 1, 3 * (lines.size() / 4)), lower, higher);
|
||||
});
|
||||
worker[2].start();
|
||||
worker[3] = new Thread(() -> {
|
||||
work(lines.subList(3 * (lines.size() / 4) + 1, (lines.size())), lower, higher);
|
||||
});
|
||||
worker[3].start();
|
||||
|
||||
|
||||
for (Thread t : worker) {
|
||||
try {
|
||||
t.join();
|
||||
} catch (InterruptedException e) {
|
||||
Logging.logError(e.getMessage(), e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
work(lines, lower, higher);
|
||||
public static IntersectionComputer getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new IntersectionComputer();
|
||||
Logging.logInfo("Created instance of IntersectionComputer");
|
||||
}
|
||||
|
||||
return new ArrayList<>(intersections);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return Liste der Schnittpunkte
|
||||
*/
|
||||
public ArrayList<Point> compute() {
|
||||
return compute(-99999, 99999);
|
||||
}
|
||||
|
||||
private void work(List<Line> lines, double lower, double higher) {
|
||||
IntersectionCounter counter = new IntersectionCounter();
|
||||
int count = counter.run(lines, new Interval(lower, higher));
|
||||
ArrayList<Point> points = counter.calculateIntersectionAbscissas();
|
||||
for (Point p : points) {
|
||||
if (!isFound(p)) {
|
||||
addIntersection(p);
|
||||
setRanges(p);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronisierter Zugriff auf die Liste der Geraden
|
||||
*
|
||||
* @return Liste der Geraden
|
||||
*/
|
||||
public synchronized List<Line> getLines() {
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronisierter hinzufügen eines Schnittpunkts
|
||||
*
|
||||
* @param p Schnittpunkt
|
||||
*/
|
||||
public synchronized void addIntersection(Point p) {
|
||||
this.intersections.add(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronisiertes abfragen ob ein Schnittpunkt bereits gefunden wurde.
|
||||
*
|
||||
* @param p Schnittpunkt
|
||||
* @return <code>true</code>, falls der Schnittpunkt p bereits gefunden wurde
|
||||
*/
|
||||
public synchronized boolean isFound(Point p) {
|
||||
return intersections.contains(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zu einem Punkt werden die minimalen, maximalen x- und y-Koordinaten überprüft und ggf.
|
||||
* neu gesetzt
|
||||
*
|
||||
* @param point Schnittpunkt
|
||||
*/
|
||||
public void setRanges(Point point) {
|
||||
xMaximum = (point.getX() > xMaximum) ? point.getX() : xMaximum;
|
||||
xMinimum = (point.getX() < xMinimum) ? point.getX() : xMinimum;
|
||||
yMaximum = (point.getY() > yMaximum) ? point.getY() : yMaximum;
|
||||
yMinimum = (point.getY() < yMinimum) ? point.getY() : yMinimum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronisierter Zugriff auf die minimale x-Koordiante
|
||||
*
|
||||
* @return minimale x-Koordiante
|
||||
*/
|
||||
public synchronized Double getxMinimum() {
|
||||
return xMinimum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronisierter Zugriff auf die maximale x-Koordinate
|
||||
*
|
||||
* @return maximale x-Koordinate
|
||||
*/
|
||||
public synchronized Double getxMaximum() {
|
||||
return xMaximum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronisierter Zugriff auf die minimale y-Koordinate
|
||||
*
|
||||
* @return minimale y-Koordinate
|
||||
*/
|
||||
public synchronized Double getyMinimum() {
|
||||
return yMinimum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronisierter Zugriff auf die maximale y-Koordinate
|
||||
*
|
||||
* @return maximale y-Koordinate
|
||||
*/
|
||||
public synchronized Double getyMaximum() {
|
||||
return yMaximum;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,20 +210,43 @@ public class IntersectionCounter {
|
|||
* @param sampledLine eine spezielle Gerade
|
||||
* @return Liste mit x Koordinaten der Schnittpunkte
|
||||
*/
|
||||
public List<Double> calculateIntersectionAbscissas(List<Line> set, Line sampledLine) {
|
||||
public List<Double> calculateIntersectionAbscissas(List<Line> set, Line sampledLine, double lower, double upper) {
|
||||
List<Line> lines = new LinkedList<>(set);
|
||||
List<Double> intersections = new ArrayList<>();
|
||||
double intersection;
|
||||
Set<Double> intersections = new HashSet<>();
|
||||
|
||||
for (Line line : lines) {
|
||||
if (line != sampledLine) {
|
||||
intersection = (line.getB() - sampledLine.getB()) / (sampledLine.getM() - line.getM());
|
||||
intersections.add(intersection);
|
||||
if (sampledLine.doIntersect(line, lower, upper)){
|
||||
intersections.add(sampledLine.intersect(line).getX());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return intersections;
|
||||
return new ArrayList<>(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> calculateIntersectionYInterception(List<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).getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayList<>(intersections);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ import org.apache.log4j.Logger;
|
|||
public class Logging {
|
||||
private static Logger logger = Logger.getRootLogger();
|
||||
|
||||
private Logging() {
|
||||
}
|
||||
|
||||
public static void logInfo(final String message, Throwable throwable) {
|
||||
logger.info(message, throwable);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package de.wwwu.awolf.presenter.util;
|
|||
import de.wwwu.awolf.model.Line;
|
||||
import de.wwwu.awolf.model.Point;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -29,38 +30,14 @@ public class RandomSampler {
|
|||
* @param r Anzahl der zu wählenden Geraden
|
||||
* @return <code>r</code> Elementige zufällige Stichprobe an Geraden
|
||||
*/
|
||||
public static List<Line> run(List<Line> set, Double r, Integer indexOfEnd) {
|
||||
|
||||
List<Line> sampledLines = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < r; i++) {
|
||||
sampledLines.add(set.get(random.nextInt(indexOfEnd)));
|
||||
public static <T extends Object> List<T> run(List<T> set, Double r) {
|
||||
int index = 0;
|
||||
List<T> sampled = new ArrayList<>();
|
||||
for (int i=0; i<r; i++) {
|
||||
index = random.nextInt(set.size());
|
||||
sampled.add(set.get(index));
|
||||
}
|
||||
|
||||
return sampledLines;
|
||||
return sampled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Diese Methode liefert eine <code>r</code> Elementige zufällige Stichprobe van Schnittpunkten.
|
||||
*
|
||||
* @param set Die gesammtmenge der Geraden aus denen gewählt werden soll
|
||||
* @param r Anzahl der zu wählenden Geraden
|
||||
* @return <code>r</code> Elementige zufällige Stichprobe an Schnittpunkten
|
||||
*/
|
||||
public static List<Double> run(List<Point> set, Double r) {
|
||||
|
||||
List<Double> sampledLines = new ArrayList<>();
|
||||
for (Point p : set) {
|
||||
sampledLines.add(p.getX());
|
||||
}
|
||||
|
||||
int inputSize = sampledLines.size();
|
||||
for (int i = 0; i < r; i++) {
|
||||
int indexToSwap = i + random.nextInt(inputSize - i);
|
||||
Double temp = sampledLines.get(i);
|
||||
sampledLines.set(i, sampledLines.get(indexToSwap));
|
||||
sampledLines.set(indexToSwap, temp);
|
||||
}
|
||||
return new ArrayList<>(sampledLines.subList(0, r.intValue()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,6 @@ import javax.imageio.ImageIO;
|
|||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -91,36 +89,6 @@ public class MainFrame extends JFrame {
|
|||
* visualisierungs methoden
|
||||
******************************************************************************************************************/
|
||||
|
||||
/**
|
||||
* Erzeugt den Dialog für die duale Darstellung
|
||||
*/
|
||||
public void createDualityDialog() {
|
||||
arrangement = new DualityPanel();
|
||||
arrangementDialog = new JDialog();
|
||||
arrangementDialog.setTitle("Dualraum - Dialog");
|
||||
arrangementDialog.setMinimumSize(new Dimension(1024, 768));
|
||||
arrangementDialog.setPreferredSize(new Dimension(800, 800));
|
||||
arrangementDialog.setLocationRelativeTo(this);
|
||||
arrangementDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||
arrangementButton.addActionListener(new DualityVisualizerListener((Presenter) getPresenter()));
|
||||
arrangement.clear();
|
||||
arrangement.setPrameters(getPresenter().getModel().getLines(),
|
||||
getPresenter().getModel().getNodes(),
|
||||
getPresenter().getModel().getxMinimum(),
|
||||
getPresenter().getModel().getxMaximum(),
|
||||
getPresenter().getModel().getyMinimum(),
|
||||
getPresenter().getModel().getyMaximum());
|
||||
arrangement.createArrangement();
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
arrangementDialog.add(arrangement);
|
||||
arrangementDialog.setLocationRelativeTo(this);
|
||||
arrangementDialog.revalidate();
|
||||
arrangementDialog.repaint();
|
||||
arrangementDialog.setVisible(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Visualisiert das Ergebnis des LMS-Schätzers
|
||||
*
|
||||
|
@ -427,12 +395,11 @@ public class MainFrame extends JFrame {
|
|||
menu.addActionListeners();
|
||||
|
||||
//action listener für diese Klasse
|
||||
arrangementButton.addActionListener(new DualityVisualizerListener((Presenter) getPresenter()));
|
||||
lmsPanel.getStartButton().addActionListener(new StartAlgorithmListener(((Presenter) getPresenter()), lmsPanel));
|
||||
rmPanel.getStartButton().addActionListener(new StartAlgorithmListener(((Presenter) getPresenter()), rmPanel));
|
||||
tsPanel.getStartButton().addActionListener(new StartAlgorithmListener(((Presenter) getPresenter()), tsPanel));
|
||||
importButton.addActionListener(new ImportDataListener((Presenter) this.getPresenter(), this));
|
||||
exportButton.addActionListener(new ExportDataListener((Presenter) this.getPresenter(), this));
|
||||
importButton.addActionListener(new ImportDataListener((Presenter) this.getPresenter()));
|
||||
exportButton.addActionListener(new ExportDataListener((Presenter) this.getPresenter()));
|
||||
generateButton.addActionListener(new GenerateDataListener((Presenter) this.getPresenter()));
|
||||
|
||||
}
|
||||
|
|
|
@ -75,11 +75,13 @@ public class MenuBar {
|
|||
this.exitItem.addActionListener(e -> {
|
||||
System.exit(0);
|
||||
});
|
||||
|
||||
this.evaluateItem.addActionListener(e -> {
|
||||
view.showEvauluationDialog();
|
||||
});
|
||||
this.importItem.addActionListener(new ImportDataListener((Presenter) view.getPresenter(), view));
|
||||
this.exportItem.addActionListener(new ExportDataListener((Presenter) view.getPresenter(), view));
|
||||
|
||||
this.importItem.addActionListener(new ImportDataListener((Presenter) view.getPresenter()));
|
||||
this.exportItem.addActionListener(new ExportDataListener((Presenter) view.getPresenter()));
|
||||
this.generateItem.addActionListener(new GenerateDataListener((Presenter) view.getPresenter()));
|
||||
this.aboutItem.addActionListener(e -> {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
package de.wwwu.awolf.view.listener;
|
||||
|
||||
import de.wwwu.awolf.presenter.Presenter;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
/**
|
||||
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
|
||||
*
|
||||
* @Author: Armin Wolf
|
||||
* @Email: a_wolf28@uni-muenster.de
|
||||
* @Date: 10.09.2017.
|
||||
*/
|
||||
public class DualityVisualizerListener implements ActionListener {
|
||||
|
||||
private Presenter presenter;
|
||||
|
||||
/**
|
||||
* Konstruktor
|
||||
*
|
||||
* @param presenter Presenter
|
||||
*/
|
||||
public DualityVisualizerListener(Presenter presenter) {
|
||||
this.presenter = presenter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
presenter.visualizeDualLines();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package de.wwwu.awolf.view.listener;
|
||||
|
||||
import de.wwwu.awolf.presenter.Presenter;
|
||||
import de.wwwu.awolf.presenter.util.Logging;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
|
@ -20,36 +21,32 @@ import java.io.File;
|
|||
public class ExportDataListener implements ActionListener {
|
||||
|
||||
private Presenter presenter;
|
||||
private Container component;
|
||||
|
||||
/**
|
||||
* Konstruktor
|
||||
*
|
||||
* @param presenter Presenter
|
||||
* @param component visuelle Elternkomponente
|
||||
*/
|
||||
public ExportDataListener(Presenter presenter, Container component) {
|
||||
public ExportDataListener(Presenter presenter) {
|
||||
this.presenter = presenter;
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
File file = null;
|
||||
JFileChooser chooser = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory());
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
chooser.setPreferredSize(new Dimension(800, 700));
|
||||
chooser.setFileFilter(new FileNameExtensionFilter("Comma-Separated Value, (*.csv)", "csv", "text"));
|
||||
chooser.setMultiSelectionEnabled(false);
|
||||
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||
|
||||
|
||||
if (chooser.showSaveDialog(component) == JFileChooser.APPROVE_OPTION) {
|
||||
//Logging.logInfo ("Datei "+chooser.getSelectedFile()+ " ausgewählt.");
|
||||
if (chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
|
||||
Logging.logInfo ("Datei "+chooser.getSelectedFile()+ " ausgewählt.");
|
||||
file = chooser.getSelectedFile();
|
||||
String filename = file.getAbsolutePath().contains(".csv") ? file.getAbsolutePath() : file.getAbsolutePath().concat(".csv");
|
||||
File withExtension = new File(filename);
|
||||
final File input = withExtension;
|
||||
final File input = new File(filename);
|
||||
Thread t = new Thread(() -> presenter.startExport(input));
|
||||
t.start();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package de.wwwu.awolf.view.listener;
|
|||
|
||||
import de.wwwu.awolf.presenter.Presenter;
|
||||
import de.wwwu.awolf.presenter.data.DataProvider;
|
||||
import de.wwwu.awolf.presenter.util.Logging;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
@ -36,6 +37,7 @@ public class GenerateDataListener implements ActionListener {
|
|||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
Logging.logInfo("Thread: " + Thread.currentThread().getName());
|
||||
if (dialog == null || !dialog.isVisible()) {
|
||||
dialog = new JDialog();
|
||||
dialog.setTitle("generiere Datensatz");
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package de.wwwu.awolf.view.listener;
|
||||
|
||||
import de.wwwu.awolf.presenter.Presenter;
|
||||
import de.wwwu.awolf.presenter.util.Logging;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
|
@ -20,32 +21,29 @@ import java.io.File;
|
|||
public class ImportDataListener implements ActionListener {
|
||||
|
||||
private Presenter presenter;
|
||||
private Container component;
|
||||
|
||||
/**
|
||||
* Konstruktor
|
||||
*
|
||||
* @param presenter Presenter
|
||||
* @param component visuelle Elternkomponente
|
||||
*/
|
||||
public ImportDataListener(Presenter presenter, Container component) {
|
||||
public ImportDataListener(Presenter presenter) {
|
||||
this.presenter = presenter;
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
File file = null;
|
||||
JFileChooser chooser = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory());
|
||||
Logging.logInfo("Thread: " + Thread.currentThread().getName());
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
chooser.setPreferredSize(new Dimension(800, 700));
|
||||
chooser.setFileFilter(new FileNameExtensionFilter("Comma-Separated Value, (*.csv)", "csv", "text"));
|
||||
|
||||
chooser.setMultiSelectionEnabled(false);
|
||||
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||
|
||||
|
||||
if (chooser.showOpenDialog(component) == JFileChooser.APPROVE_OPTION) {
|
||||
File file = null;
|
||||
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
|
||||
//Logging.logInfo ("Datei "+chooser.getSelectedFile()+ " ausgewählt.");
|
||||
file = chooser.getSelectedFile();
|
||||
final File input = file;
|
||||
|
@ -53,5 +51,6 @@ public class ImportDataListener implements ActionListener {
|
|||
t.start();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import de.wwwu.awolf.model.Line;
|
|||
import de.wwwu.awolf.presenter.Presenter;
|
||||
import de.wwwu.awolf.presenter.algorithms.Algorithm;
|
||||
import de.wwwu.awolf.presenter.data.DataProvider;
|
||||
import de.wwwu.awolf.presenter.util.Logging;
|
||||
import de.wwwu.awolf.view.MainFrame;
|
||||
import de.wwwu.awolf.view.custom.ButtonGroupAtLeastTwo;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Root logger option
|
||||
log4j.rootLogger=INFO, file, stdout
|
||||
log4j.rootLogger=DEBUG, file, stdout
|
||||
# Direct log messages to a log file
|
||||
log4j.appender.file=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.file.File=D:\\Git\\master-implementierung\\LinearRegressionTool\\log4j\\app.log
|
||||
|
|
|
@ -23,7 +23,6 @@ import static org.junit.Assert.*;
|
|||
public class LeastMedianOfSquaresEstimatorTest {
|
||||
|
||||
private LeastMedianOfSquaresEstimator lms;
|
||||
private IntersectionComputer intersectionComputer;
|
||||
|
||||
@Before
|
||||
public void setUp(){
|
||||
|
@ -32,15 +31,12 @@ public class LeastMedianOfSquaresEstimatorTest {
|
|||
|
||||
|
||||
LinkedList<Line> lines = new LinkedList<>();
|
||||
ArrayList<Point> intersections = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lines.add(new Line(x[i], y[i]));
|
||||
}
|
||||
|
||||
intersectionComputer = new IntersectionComputer(lines);
|
||||
intersections = intersectionComputer.compute();
|
||||
lms = new LeastMedianOfSquaresEstimator(lines, intersections);
|
||||
lms = new LeastMedianOfSquaresEstimator(lines);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ import static org.junit.Assert.*;
|
|||
public class TheilSenEstimatorTest {
|
||||
|
||||
TheilSenEstimator estimator;
|
||||
IntersectionComputer intersectionComputer;
|
||||
|
||||
@Before
|
||||
public void setUp(){
|
||||
|
@ -32,15 +31,12 @@ public class TheilSenEstimatorTest {
|
|||
|
||||
|
||||
List<Line> lines = new LinkedList<>();
|
||||
List<Point> intersections = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lines.add(new Line(x[i], y[i]));
|
||||
}
|
||||
|
||||
intersectionComputer = new IntersectionComputer(lines);
|
||||
intersections = intersectionComputer.compute();
|
||||
estimator = new TheilSenEstimator(lines, intersections);
|
||||
estimator = new TheilSenEstimator(lines);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
package de.wwwu.awolf.presenter.util;
|
||||
|
||||
import de.wwwu.awolf.model.Line;
|
||||
import de.wwwu.awolf.model.LineModel;
|
||||
import de.wwwu.awolf.model.Point;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
|
||||
*
|
||||
* @Author: Armin Wolf
|
||||
* @Email: a_wolf28@uni-muenster.de
|
||||
* @Date: 23.10.2017.
|
||||
*/
|
||||
public class IntersectionComputerTest {
|
||||
|
||||
private IntersectionComputer intersectionComputer;
|
||||
private LineModel lineModel;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
lineModel = new LineModel();
|
||||
lineModel.addLine(new Line(3,13,10,3));
|
||||
lineModel.addLine(new Line(1,9,1,9));
|
||||
lineModel.addLine(new Line(1,12,4,6));
|
||||
intersectionComputer = new IntersectionComputer(lineModel.getLines());
|
||||
}
|
||||
@Test
|
||||
public void compute() throws Exception {
|
||||
|
||||
List<Point> intersections = intersectionComputer.compute();
|
||||
double[] expectedX = {4.66, 7.11, 9.39};
|
||||
double[] expectedY = {4.66, 7.11, 5.52};
|
||||
|
||||
double[] actualX = new double[3];
|
||||
double[] actualY = new double[3];
|
||||
|
||||
for (int i=0;i<3;i++){
|
||||
actualX[i] = intersections.get(i).getX();
|
||||
actualY[i] = intersections.get(i).getY();
|
||||
}
|
||||
|
||||
Arrays.sort(expectedX);
|
||||
Arrays.sort(actualX);
|
||||
assertArrayEquals(expectedX, actualX, 0.01d);
|
||||
|
||||
Arrays.sort(expectedY);
|
||||
Arrays.sort(actualY);
|
||||
assertArrayEquals(expectedY, actualY, 0.01d);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue