From 30ff17328ec778b393c7b6be20b166d0f42cf187 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 15 Sep 2017 20:18:36 +0200 Subject: [PATCH] =?UTF-8?q?naive=20Implementierung=20der=20Algortihmen=20f?= =?UTF-8?q?=C3=BCr=20die=20Evaluation.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/presenter/AbstractPresenter.java | 2 +- src/main/java/presenter/Presenter.java | 6 +- .../LeastMedianOfSquaresEstimator.java | 4 +- .../RepeatedMedianEstimator.java | 6 +- .../{ => advanced}/TheilSenEstimator.java | 6 +- .../NaivLeastMedianOfSquaresEstimator.java | 90 +++++++++++++ .../naiv/NaivRepeatedMedianEstimator.java | 121 ++++++++++++++++++ .../naiv/NaivTheilSenEstimator.java | 75 +++++++++++ .../{ => util}/BinomialCoeffizient.java | 2 +- .../{ => util}/FastElementSelector.java | 2 +- .../{ => util}/IntersectionCounter.java | 2 +- .../algorithms/{ => util}/RandomSampler.java | 2 +- .../evaluation/EvaluateAlgorithms.java | 24 +++- .../PercentageErrorBasedMeasure.java | 2 +- .../evaluation/ScaleDependentMeasure.java | 2 +- .../evaluation/ScaledErrorBasedMeasure.java | 2 +- .../view/listener/StartAlgorithmListener.java | 2 - src/main/resources/Thumbs.db | Bin 20992 -> 21504 bytes .../LeastMedianOfSquaresEstimatorTest.java | 2 + 19 files changed, 332 insertions(+), 20 deletions(-) rename src/main/java/presenter/algorithms/{ => advanced}/LeastMedianOfSquaresEstimator.java (99%) rename src/main/java/presenter/algorithms/{ => advanced}/RepeatedMedianEstimator.java (98%) rename src/main/java/presenter/algorithms/{ => advanced}/TheilSenEstimator.java (97%) create mode 100644 src/main/java/presenter/algorithms/naiv/NaivLeastMedianOfSquaresEstimator.java create mode 100644 src/main/java/presenter/algorithms/naiv/NaivRepeatedMedianEstimator.java create mode 100644 src/main/java/presenter/algorithms/naiv/NaivTheilSenEstimator.java rename src/main/java/presenter/algorithms/{ => util}/BinomialCoeffizient.java (93%) rename src/main/java/presenter/algorithms/{ => util}/FastElementSelector.java (98%) rename src/main/java/presenter/algorithms/{ => util}/IntersectionCounter.java (99%) rename src/main/java/presenter/algorithms/{ => util}/RandomSampler.java (98%) diff --git a/src/main/java/presenter/AbstractPresenter.java b/src/main/java/presenter/AbstractPresenter.java index 6de3500..0582db9 100644 --- a/src/main/java/presenter/AbstractPresenter.java +++ b/src/main/java/presenter/AbstractPresenter.java @@ -3,7 +3,7 @@ package presenter; import model.Interval; import model.Line; import model.LineModel; -import presenter.algorithms.IntersectionCounter; +import presenter.algorithms.util.IntersectionCounter; import presenter.evaluation.EvaluateAlgorithms; import view.MainFrame; diff --git a/src/main/java/presenter/Presenter.java b/src/main/java/presenter/Presenter.java index 6ac4af5..f429b55 100644 --- a/src/main/java/presenter/Presenter.java +++ b/src/main/java/presenter/Presenter.java @@ -1,9 +1,9 @@ package presenter; import model.LineModel; -import presenter.algorithms.LeastMedianOfSquaresEstimator; -import presenter.algorithms.RepeatedMedianEstimator; -import presenter.algorithms.TheilSenEstimator; +import presenter.algorithms.advanced.LeastMedianOfSquaresEstimator; +import presenter.algorithms.advanced.RepeatedMedianEstimator; +import presenter.algorithms.advanced.TheilSenEstimator; import presenter.evaluation.EvaluateAlgorithms; import presenter.generator.DatasetGenerator; import presenter.io.DataExporter; diff --git a/src/main/java/presenter/algorithms/LeastMedianOfSquaresEstimator.java b/src/main/java/presenter/algorithms/advanced/LeastMedianOfSquaresEstimator.java similarity index 99% rename from src/main/java/presenter/algorithms/LeastMedianOfSquaresEstimator.java rename to src/main/java/presenter/algorithms/advanced/LeastMedianOfSquaresEstimator.java index 42d8218..db6f0e1 100644 --- a/src/main/java/presenter/algorithms/LeastMedianOfSquaresEstimator.java +++ b/src/main/java/presenter/algorithms/advanced/LeastMedianOfSquaresEstimator.java @@ -1,9 +1,11 @@ -package presenter.algorithms; +package presenter.algorithms.advanced; import model.Interval; import model.Line; import model.Point; import presenter.Presenter; +import presenter.algorithms.Algorithm; +import presenter.algorithms.util.IntersectionCounter; import java.util.*; diff --git a/src/main/java/presenter/algorithms/RepeatedMedianEstimator.java b/src/main/java/presenter/algorithms/advanced/RepeatedMedianEstimator.java similarity index 98% rename from src/main/java/presenter/algorithms/RepeatedMedianEstimator.java rename to src/main/java/presenter/algorithms/advanced/RepeatedMedianEstimator.java index 7b87e08..c6d1c8f 100644 --- a/src/main/java/presenter/algorithms/RepeatedMedianEstimator.java +++ b/src/main/java/presenter/algorithms/advanced/RepeatedMedianEstimator.java @@ -1,8 +1,12 @@ -package presenter.algorithms; +package presenter.algorithms.advanced; import model.Interval; import model.Line; import presenter.Presenter; +import presenter.algorithms.Algorithm; +import presenter.algorithms.util.FastElementSelector; +import presenter.algorithms.util.IntersectionCounter; +import presenter.algorithms.util.RandomSampler; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/presenter/algorithms/TheilSenEstimator.java b/src/main/java/presenter/algorithms/advanced/TheilSenEstimator.java similarity index 97% rename from src/main/java/presenter/algorithms/TheilSenEstimator.java rename to src/main/java/presenter/algorithms/advanced/TheilSenEstimator.java index cf2b046..321d311 100644 --- a/src/main/java/presenter/algorithms/TheilSenEstimator.java +++ b/src/main/java/presenter/algorithms/advanced/TheilSenEstimator.java @@ -1,9 +1,13 @@ -package presenter.algorithms; +package presenter.algorithms.advanced; import model.Interval; import model.Line; import model.Point; import presenter.Presenter; +import presenter.algorithms.Algorithm; +import presenter.algorithms.util.BinomialCoeffizient; +import presenter.algorithms.util.FastElementSelector; +import presenter.algorithms.util.RandomSampler; import java.util.*; diff --git a/src/main/java/presenter/algorithms/naiv/NaivLeastMedianOfSquaresEstimator.java b/src/main/java/presenter/algorithms/naiv/NaivLeastMedianOfSquaresEstimator.java new file mode 100644 index 0000000..de93ce8 --- /dev/null +++ b/src/main/java/presenter/algorithms/naiv/NaivLeastMedianOfSquaresEstimator.java @@ -0,0 +1,90 @@ +package presenter.algorithms.naiv; + +import model.Line; +import model.Point; +import presenter.algorithms.Algorithm; +import presenter.algorithms.util.FastElementSelector; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; + +/** + * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden. + * + * @Author: Armin Wolf + * @Email: a_wolf28@uni-muenster.de + * @Date: 15.09.2017. + */ +public class NaivLeastMedianOfSquaresEstimator implements Algorithm { + private ArrayList set = new ArrayList<>(); + + private Integer n; + private Double ds,as,bs; + + public NaivLeastMedianOfSquaresEstimator(LinkedList lines) { + for (Line l :lines){ + set.add(new Point(l.getM(),l.getB())); + } + } + + + private void crudeAlg(){ + ds = Double.MAX_VALUE; + as = 0d; + bs = 0d; + ArrayList triple = new ArrayList<>(); + Double beta; + Double alpha; + Double dijk; + for (Point i : set){ + for (Point j : set) { + for (Point k : set){ + triple.add(i); + triple.add(j); + triple.add(k); + Collections.sort(triple); + beta = (triple.get(0).getY() - triple.get(2).getY()) / (triple.get(0).getX() - triple.get(2).getX()); + alpha = (triple.get(1).getY() + triple.get(2).getY() - ( beta * (triple.get(1).getX() + triple.get(2).getX()))) / 2f; + dijk = f(alpha, beta); + if (dijk < ds){ + ds = dijk; + as = alpha; + bs = beta; + // System.out.printf("Distanz: %6.2f\tAlpha: %6.2f\tBeta: %6.2f",ds,as,bs); + } + triple.clear(); + } + } + } + System.out.printf("Naiv LMS: %6.2f * x + %6.3f\n",bs, as); + } + + + + private Double f(Double a, Double b){ + ArrayList res = new ArrayList<>(); + for (Point p : set){ + res.add(Math.abs(p.getY() - (a + b * p.getX()))); + } + return FastElementSelector.randomizedSelect(res, res.size()/2); + } + + @Override + public void run() { + crudeAlg(); + } + + @Override + public void getResult() { + + } + + public Double getAs() { + return as; + } + + public Double getBs() { + return bs; + } +} diff --git a/src/main/java/presenter/algorithms/naiv/NaivRepeatedMedianEstimator.java b/src/main/java/presenter/algorithms/naiv/NaivRepeatedMedianEstimator.java new file mode 100644 index 0000000..1e2f229 --- /dev/null +++ b/src/main/java/presenter/algorithms/naiv/NaivRepeatedMedianEstimator.java @@ -0,0 +1,121 @@ +package presenter.algorithms.naiv; + +import model.Line; +import model.Point; +import presenter.algorithms.Algorithm; +import presenter.algorithms.util.FastElementSelector; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; + +/** + * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden. + * + * @Author: Armin Wolf + * @Email: a_wolf28@uni-muenster.de + * @Date: 15.09.2017. + */ +public class NaivRepeatedMedianEstimator implements Algorithm{ + + private LinkedList lines; + private HashMap> slopesPerLine; + private HashMap> interceptPerLine; + private ArrayList xMedians, yMedians; + private Double medianX; + private Double medianY; + + public NaivRepeatedMedianEstimator(LinkedList lines) { + this.lines = lines; + slopesPerLine = new HashMap<>(); + interceptPerLine = new HashMap<>(); + xMedians = new ArrayList<>(); + yMedians = new ArrayList<>(); + } + + @Override + public void run() { + //init the List for the slopes + for (int j=0;j()); + } + if (interceptPerLine.get(leq.getId()) == null) { + interceptPerLine.put(leq.getId(), new ArrayList<>()); + } + } + + + //calculate all slopes for each line + Point ret; + for (int i=0;i list = slopesPerLine.get(l); + int size = list.size() / 2; + if (size > 0) { + Double medianX = FastElementSelector.randomizedSelect(list, size); + xMedians.add(medianX); + } + } + + //calculate median of slopes for each line + for (String l : interceptPerLine.keySet()) { + ArrayList list = interceptPerLine.get(l); + int size = list.size() / 2; + if (size > 0) { + Double medianY = FastElementSelector.randomizedSelect(list, size); + yMedians.add(medianY); + } + } + + medianX = FastElementSelector.randomizedSelect(xMedians, xMedians.size()/2); + medianY = FastElementSelector.randomizedSelect(yMedians, yMedians.size()/2); + System.out.printf("Naiv RM: %6.2f * x + %6.2f \n", medianX, medianY); + } + + @Override + public void getResult() { + + } + + private Point calculateSlope(Line lineA, Line lineB){ + Double xi; + Double xj; + Double yi; + Double yj; + + if (lineB.getM() > lineA.getM()){ + xi = lineA.getM(); + yi = lineA.getB(); + xj = lineB.getM(); + yj = lineB.getB(); + } else { + xj = lineA.getM(); + yj = lineA.getB(); + xi = lineB.getM(); + yi = lineB.getB(); + } + + + Double m = (yj - yi) / (xj -xi); + Double b = ((xj * yi) - (xi * yj)) / (xj - xi); + return new Point(m,b); + } + + public Double getMedianX() { + return medianX; + } + + public Double getMedianY() { + return medianY; + } +} diff --git a/src/main/java/presenter/algorithms/naiv/NaivTheilSenEstimator.java b/src/main/java/presenter/algorithms/naiv/NaivTheilSenEstimator.java new file mode 100644 index 0000000..f0e8c15 --- /dev/null +++ b/src/main/java/presenter/algorithms/naiv/NaivTheilSenEstimator.java @@ -0,0 +1,75 @@ +package presenter.algorithms.naiv; + +import model.Line; +import presenter.algorithms.Algorithm; +import presenter.algorithms.util.FastElementSelector; + +import java.util.ArrayList; +import java.util.LinkedList; + +/** + * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden. + * + * @Author: Armin Wolf + * @Email: a_wolf28@uni-muenster.de + * @Date: 15.09.2017. + */ +public class NaivTheilSenEstimator implements Algorithm { + + + private LinkedList lines; + private Double slope; + private Double yInterception; + + public NaivTheilSenEstimator(LinkedList lines) { + this.lines = lines; + this.slope = 0d; + this.yInterception = 0d; + } + + + @Override + public void run() { + ArrayList slopesList = new ArrayList<>(); + int cnt = 0; + for (int i = 0; i < lines.size(); i++) { + double x = lines.get(i).getM(); + double y = lines.get(i).getB(); + + for (int j = i + 1; j < lines.size(); j++) { + if (x != lines.get(j).getM()) { // x must be different, otherwise slope becomes infinite + Double slope = (lines.get(j).getB() - y) / (lines.get(j).getM() - x); + slopesList.add(slope); + ++cnt; + } + } + } + + + ArrayList list1 = new ArrayList<>(); + ArrayList list2 = new ArrayList<>(); + for (int i=0;i result; ArrayList> multipleResults = new ArrayList<>(); + /* DEBUG */ + + NaivLeastMedianOfSquaresEstimator l = new NaivLeastMedianOfSquaresEstimator(arrangement.getLines()); + NaivRepeatedMedianEstimator r = new NaivRepeatedMedianEstimator(arrangement.getLines()); + NaivTheilSenEstimator t = new NaivTheilSenEstimator(arrangement.getLines()); + + l.run(); + r.run(); + t.run(); + System.out.println(); + + + /* DEBUG */ startLMS(); startRM(); startTS(); diff --git a/src/main/java/presenter/evaluation/PercentageErrorBasedMeasure.java b/src/main/java/presenter/evaluation/PercentageErrorBasedMeasure.java index 05a9594..2525b1b 100644 --- a/src/main/java/presenter/evaluation/PercentageErrorBasedMeasure.java +++ b/src/main/java/presenter/evaluation/PercentageErrorBasedMeasure.java @@ -1,7 +1,7 @@ package presenter.evaluation; import model.Line; -import presenter.algorithms.FastElementSelector; +import presenter.algorithms.util.FastElementSelector; import java.util.ArrayList; import java.util.LinkedList; diff --git a/src/main/java/presenter/evaluation/ScaleDependentMeasure.java b/src/main/java/presenter/evaluation/ScaleDependentMeasure.java index b78807e..0733298 100644 --- a/src/main/java/presenter/evaluation/ScaleDependentMeasure.java +++ b/src/main/java/presenter/evaluation/ScaleDependentMeasure.java @@ -1,7 +1,7 @@ package presenter.evaluation; import model.Line; -import presenter.algorithms.FastElementSelector; +import presenter.algorithms.util.FastElementSelector; import java.util.ArrayList; import java.util.LinkedList; diff --git a/src/main/java/presenter/evaluation/ScaledErrorBasedMeasure.java b/src/main/java/presenter/evaluation/ScaledErrorBasedMeasure.java index f41e25c..da5eafa 100644 --- a/src/main/java/presenter/evaluation/ScaledErrorBasedMeasure.java +++ b/src/main/java/presenter/evaluation/ScaledErrorBasedMeasure.java @@ -1,7 +1,7 @@ package presenter.evaluation; import model.Line; -import presenter.algorithms.FastElementSelector; +import presenter.algorithms.util.FastElementSelector; import java.util.ArrayList; import java.util.LinkedList; diff --git a/src/main/java/view/listener/StartAlgorithmListener.java b/src/main/java/view/listener/StartAlgorithmListener.java index b9e28e8..d68a980 100644 --- a/src/main/java/view/listener/StartAlgorithmListener.java +++ b/src/main/java/view/listener/StartAlgorithmListener.java @@ -1,8 +1,6 @@ package view.listener; import presenter.Presenter; -import presenter.algorithms.Algorithm; -import presenter.algorithms.LeastMedianOfSquaresEstimator; import view.panels.tabs.LMSPanel; import view.panels.tabs.RMPanel; import view.panels.tabs.TSPanel; diff --git a/src/main/resources/Thumbs.db b/src/main/resources/Thumbs.db index 9919d7eaffb03a4bbbe1d01cf7138c0f5ba0a7a8..bac6b2cf92eaac648fad804b986f074b613f6423 100644 GIT binary patch delta 3537 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