diff --git a/src/main/java/Presenter/Algorithms/LeastMedianOfSquaresEstimator.java b/src/main/java/Presenter/Algorithms/LeastMedianOfSquaresEstimator.java index 7cc541f..73e0182 100644 --- a/src/main/java/Presenter/Algorithms/LeastMedianOfSquaresEstimator.java +++ b/src/main/java/Presenter/Algorithms/LeastMedianOfSquaresEstimator.java @@ -3,8 +3,7 @@ package Presenter.Algorithms; import Model.Line; import Model.Point; import Model.Slab; -import Presenter.InversionCounter; -import Presenter.Presenter; +import Presenter.*; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -27,7 +26,7 @@ public class LeastMedianOfSquaresEstimator extends Observable implements Algorit private LinkedList set = new LinkedList<>(); private LinkedList intersections = new LinkedList<>(); - private InversionCounter invCounter = new InversionCounter(); + private IntersectionCounter invCounter = new IntersectionCounter(); private int n; private double quantileError; private int kPlus; diff --git a/src/main/java/Presenter/Algorithms/RepeatedMedianEstimator.java b/src/main/java/Presenter/Algorithms/RepeatedMedianEstimator.java index 764b106..ab2cd22 100644 --- a/src/main/java/Presenter/Algorithms/RepeatedMedianEstimator.java +++ b/src/main/java/Presenter/Algorithms/RepeatedMedianEstimator.java @@ -2,8 +2,7 @@ package Presenter.Algorithms; import Model.Line; import Model.Slab; -import Presenter.InversionCounter; -import Presenter.Presenter; +import Presenter.*; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -80,10 +79,8 @@ public class RepeatedMedianEstimator extends Observable implements Algorithm { while (linesInCenterSlab.size() != 1) { n = linesInCenterSlab.size(); r = Math.ceil(Math.pow(n, beta)); - ArrayList lines = sampleLines(linesInCenterSlab, r); + ArrayList lines = RandomLineSampler.run(linesInCenterSlab, r, linesInCenterSlab.size()); - InversionCounter invCounter = new InversionCounter(); - //int intersectionCount = invCounter.run(lines, interval); //For each Sampled Line, compute its median intersection abscissa ArrayList medianIntersectionAbscissas = new ArrayList<>(); @@ -100,8 +97,8 @@ public class RepeatedMedianEstimator extends Observable implements Algorithm { computeSlabBorders(); - if (medianIntersectionAbscissas.size() < kLow || medianIntersectionAbscissas.size() intersections = new ArrayList<>(); double intersection; - for (Line line : linesInCenterSlab) { - if (line != sampledLine) { - intersection = (line.getB() - sampledLine.getB()) / (sampledLine.getM() - line.getM()); - - if (!intersectionAbscissas.get(line).contains(intersection)) - intersectionAbscissas.get(line).add(intersection); - if (!intersectionAbscissas.get(sampledLine).contains(intersection)) - intersectionAbscissas.get(sampledLine).add(intersection); - - intersections.add(intersection); - } - } + IntersectionCounter intersectionCounter = new IntersectionCounter(); + intersections = intersectionCounter.calculateIntersectionAbscissas(linesInCenterSlab, sampledLine); Collections.sort(intersections); double ki = Math.ceil((n - 1) / 2) - countLeftSlab.get(index); diff --git a/src/main/java/Presenter/Algorithms/TheilSenEstimator.java b/src/main/java/Presenter/Algorithms/TheilSenEstimator.java index 7dcfc79..96c6eb2 100644 --- a/src/main/java/Presenter/Algorithms/TheilSenEstimator.java +++ b/src/main/java/Presenter/Algorithms/TheilSenEstimator.java @@ -1,5 +1,16 @@ package Presenter.Algorithms; +import Model.Line; +import Model.Point; +import Model.Slab; +import Presenter.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Observable; +import java.util.concurrent.ThreadLocalRandom; + /** * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden. * @@ -7,6 +18,121 @@ package Presenter.Algorithms; * @Email: a_wolf28@uni-muenster.de * @Date: 28.05.2017. */ -public class TheilSenEstimator implements Algorithm { +public class TheilSenEstimator extends Observable implements Algorithm { + private Presenter presenter; + private ArrayList set; + private ArrayList intersectionSet; + private Integer numberOfLinesOnLeft; + private Slab interval; + + private Double j; + private Integer jA; + private Integer jB; + private Double r; + private Integer n; + private Double N; + private Integer k; + + private Double a; + private Double b; + private Double aVariant; + private Double bVariant; + private ArrayList sampledIntersections; + + private Double ymin = Double.MAX_VALUE; + private Double ymax = Double.MIN_VALUE; + + public TheilSenEstimator(LinkedList set, LinkedList intersectionSet, Presenter presenter) { + this.presenter = presenter; + this.intersectionSet = new ArrayList<>(intersectionSet); + this.set = new ArrayList<>(set); + this.n = set.size(); + this.sampledIntersections = new ArrayList<>(); + Double bin = BinomialCoeffizient.run(n, 2); + this.numberOfLinesOnLeft = 0; + + for (Line l : set){ + ymin = ymin > l.getB() ? l.getB() : ymin; + ymax = ymax < l.getB() ? l.getB() : ymax; + } + + this.k = Integer.valueOf((int) (bin / 2)); + this.N = bin; + } + + public void run(){ + + a = -10000d; + b = 10000d; + + interval = new Slab(a,b); + while (true){ + + if (this.N <= n){ + break; + } else { + r = Double.valueOf(n); + IntersectionCounter counter = new IntersectionCounter(); + int numberOfIntersections = counter.run(set, new Slab(-10000,a)); + j = (r /N) * (k - numberOfIntersections); + jA = (int) Math.floor(j - (3 * Math.sqrt(r))); + jB = (int) Math.floor(j + (3 * Math.sqrt(r))); + + do { + sampledIntersections = randomSampleOfIntersections(intersectionSet, r); + Collections.sort(sampledIntersections); + aVariant = sampledIntersections.get(jA); + bVariant = sampledIntersections.get(jB); + } + while (!checkCondition(sampledIntersections)); + + a = aVariant; + b = bVariant; + N = Double.valueOf(checkNumberOfIntersectionInInterval(a,b,sampledIntersections)); + } + } + + if (presenter != null) { + setChanged(); + double m = (interval.getLower() + interval.getUpper()) * (-0.5); + double min = Collections.min(sampledIntersections); + double max = Collections.max(sampledIntersections); + double avgx = (min + max) *0.5; + double avgy = (ymin + ymax) * 0.5; + double b = ((avgx * m) + avgy); + String[] result = {"rm", m+"", b+""}; + notifyObservers(result); + } + System.out.println(interval.getLower()+" <=> "+interval.getUpper()); + } + + private Boolean checkCondition(ArrayList intersections){ + Boolean cond1 = (intersections.get(k) >= aVariant) && (intersections.get(k) < bVariant); + Boolean cond2 = (checkNumberOfIntersectionInInterval(aVariant,bVariant,intersections) <= ((11 * N) / Math.sqrt(r))); + return cond1 && cond2; + } + + + public ArrayList randomSampleOfIntersections(ArrayList set, Double r){ + ArrayList sampledLines = new ArrayList<>(); + + for (int i = 0; i < r; i++) { + sampledLines.add(set.get(ThreadLocalRandom.current().nextInt(0, set.size()-1)).getX()); + } + + return sampledLines; + + } + + + public int checkNumberOfIntersectionInInterval(double a, double b, ArrayList intersections){ + int counter = 0; + for (Double x : intersections){ + if (x >= a && x < b){ + counter++; + } + } + return counter; + } } diff --git a/src/main/java/Presenter/BinomialCoeffizient.java b/src/main/java/Presenter/BinomialCoeffizient.java new file mode 100644 index 0000000..a0c8583 --- /dev/null +++ b/src/main/java/Presenter/BinomialCoeffizient.java @@ -0,0 +1,28 @@ +package Presenter; + +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: 26.06.2017. + */ +public class BinomialCoeffizient { + + public static Double run(int n, int k) { + int res = 1; + + if ( k > n - k ) + k = n - k; + + for (int i = 0; i < k; ++i){ + res *= (n - i); + res /= (i + 1); + } + + return Double.valueOf(res); + } +} diff --git a/src/main/java/Presenter/InversionCounter.java b/src/main/java/Presenter/IntersectionCounter.java similarity index 80% rename from src/main/java/Presenter/InversionCounter.java rename to src/main/java/Presenter/IntersectionCounter.java index e439a00..fff23e9 100644 --- a/src/main/java/Presenter/InversionCounter.java +++ b/src/main/java/Presenter/IntersectionCounter.java @@ -8,7 +8,9 @@ import Presenter.Comparators.YOrderLineComparatorEnd; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; +import org.jfree.xml.factory.objects.DoubleObjectDescription; /** * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden. @@ -17,7 +19,7 @@ import java.util.List; * @Email: a_wolf28@uni-muenster.de * @Date: 18.06.2017. */ -public class InversionCounter { +public class IntersectionCounter { private HashMap dictionaryTO; private HashMap dictionaryBACK; @@ -53,8 +55,6 @@ public class InversionCounter { int ret = countInversions(substituted, 0, substituted.size() - 1, temp); - getIntersectionAbscissas(); - return ret; } @@ -150,7 +150,7 @@ public class InversionCounter { } - public HashMap> getIntersectionAbscissas() { + public HashMap> getIntersectionLinePairs() { ArrayList result = new ArrayList<>(); HashMap> ret = new HashMap<>(); @@ -192,4 +192,44 @@ public class InversionCounter { return ret; } + public HashMap calculateIntersectionAbscissas(){ + ArrayList result = new ArrayList<>(); + HashMap ret = new HashMap<>(); + + for (int i = 0; i < inversions.size(); i++) { + result.add(new Pair(dictionaryBACK.get(inversions.get(i).getP1()), + dictionaryBACK.get(inversions.get(i).getP2()))); + } + ArrayList linePairs; + + for (Pair p : result) { + Line line = secondaryDictionaryBACK.get(p.getP1()); + Line sampledLine = secondaryDictionaryBACK.get(p.getP2()); + if (!line.equals(sampledLine)){ + double intersection = (line.getB() - sampledLine.getB()) / (sampledLine.getM() - line.getM()); + ret.put(intersection, intersection); + } + } + + return ret; + } + + + + public ArrayList calculateIntersectionAbscissas(ArrayList set, Line sampledLine){ + LinkedList lines = new LinkedList<>(set); + ArrayList intersections = new ArrayList<>(); + double intersection; + + for (Line line : lines) { + if (line != sampledLine) { + intersection = (line.getB() - sampledLine.getB()) / (sampledLine.getM() - line.getM()); + intersections.add(intersection); + } + } + + return intersections; + } + + } diff --git a/src/main/java/Presenter/Presenter.java b/src/main/java/Presenter/Presenter.java index 1d789c3..971294c 100644 --- a/src/main/java/Presenter/Presenter.java +++ b/src/main/java/Presenter/Presenter.java @@ -5,6 +5,7 @@ import Model.Line; import Model.Point; import Presenter.Algorithms.LeastMedianOfSquaresEstimator; import Presenter.Algorithms.RepeatedMedianEstimator; +import Presenter.Algorithms.TheilSenEstimator; import Presenter.Import.DataImporter; import View.MainFrame; import java.io.File; @@ -91,6 +92,16 @@ public class Presenter implements Observer { }); } + if (result[0] == "ts"){ + SwingUtilities.invokeLater(() -> { + getView().visualizeTS(Double.parseDouble(result[1]), Double.parseDouble(result[2])); + getView().logHeading("Theil-Sen Estimator"); + getView().log("m: " + result[1]); + getView().log("b: " + result[2]); + getView().logSuccess("Berechnung wurde Erfolgreich durchgeführt
"); + }); + } + if (result[0] == "import"){ Double max = Double.parseDouble(result[1]); Double current = Double.parseDouble(result[2]); @@ -123,7 +134,7 @@ public class Presenter implements Observer { if (input[0] != null && input[1] != null){ Double constant = Double.parseDouble(input[0]); Double error = Double.parseDouble(input[1]); - lms = new LeastMedianOfSquaresEstimator(model.getLines(), model.getNodes(), this); + lms = new LeastMedianOfSquaresEstimator(getModel().getLines(), getModel().getNodes(), this); lms.setConstant(constant); lms.setQuantileError(error); lms.addObserver(this); @@ -133,7 +144,7 @@ public class Presenter implements Observer { public void calculateRM(String input){ if (input != null){ - RepeatedMedianEstimator rm = new RepeatedMedianEstimator(this.getLines(), this); + RepeatedMedianEstimator rm = new RepeatedMedianEstimator(getModel().getLines(), this); Double parameter = Double.parseDouble(input); rm.setBeta(parameter); rm.addObserver(this); @@ -141,6 +152,15 @@ public class Presenter implements Observer { } } + + public void calculateTS(String input){ + if (input != null){ + TheilSenEstimator ts = new TheilSenEstimator(getModel().getLines(), getModel().getNodes(),this); + ts.addObserver(this); + ts.run(); + } + } + /*************************************************************************************************************************** * Hilfsmethoden ***************************************************************************************************************************/ diff --git a/src/main/java/Presenter/RandomLineSampler.java b/src/main/java/Presenter/RandomLineSampler.java new file mode 100644 index 0000000..d212efa --- /dev/null +++ b/src/main/java/Presenter/RandomLineSampler.java @@ -0,0 +1,47 @@ +package Presenter; + +import Model.Line; +import Model.Point; +import Model.Slab; +import java.util.ArrayList; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden. + * + * @Author: Armin Wolf + * @Email: a_wolf28@uni-muenster.de + * @Date: 26.06.2017. + */ +public class RandomLineSampler { + + /** + * + * @param set + * @param r + * @return + */ + public static ArrayList run(ArrayList set, Double r, Integer indexOfEnd) { + + ArrayList sampledLines = new ArrayList<>(); + + for (int i = 0; i < r; i++) { + sampledLines.add(set.get(ThreadLocalRandom.current().nextInt(0, indexOfEnd))); + } + + return sampledLines; + } + + + + /** + * + * @param set + * @param r + * @return + */ + public static ArrayList run(ArrayList set, Integer r) { + + return run(set,Double.valueOf(r), set.size()); + } +} diff --git a/src/main/java/View/MainFrame.java b/src/main/java/View/MainFrame.java index e1dbab7..03b50c1 100644 --- a/src/main/java/View/MainFrame.java +++ b/src/main/java/View/MainFrame.java @@ -6,6 +6,7 @@ import View.Panels.LMSPanel; import View.Panels.MenuPanel; import View.Panels.OutputPanel; import View.Panels.RMPanel; +import View.Panels.TSPanel; import java.awt.BorderLayout; import java.awt.ComponentOrientation; import java.awt.Container; @@ -58,6 +59,7 @@ public class MainFrame extends JFrame { private MenuPanel menupanel; private LMSPanel lmsPanel; private RMPanel rmPanel; + private TSPanel tsPanel; private JPanel pane; private JPanel northPanel; @@ -131,6 +133,12 @@ public class MainFrame extends JFrame { createPlot(m,b,plotRM,rmPanel); } + public void visualizeTS(double m, double b){ + plotTS = new PlotDialog(); + tsPanel.setPlotDialog(plotTS); + createPlot(m,b,plotTS, tsPanel); + } + public void createPlot(double m, double b, PlotDialog plot, JPanel panel){ SwingUtilities.invokeLater(() -> { plot.clear(); @@ -158,7 +166,7 @@ public class MainFrame extends JFrame { private void setupTabbedPane() { tabbedPane.add("Least Median of Squares", lmsPanel); tabbedPane.add("Repeated Median", rmPanel); - tabbedPane.add("Theil-Sen", new JPanel()); + tabbedPane.add("Theil-Sen", tsPanel); } private void addComponents() { @@ -196,6 +204,7 @@ public class MainFrame extends JFrame { this.setExtendedState(JFrame.MAXIMIZED_BOTH); lmsPanel.setMinimumSize(new Dimension(400, 500)); rmPanel.setMinimumSize(new Dimension(400, 500)); + tsPanel.setMinimumSize(new Dimension(400, 500)); output.setMinimumSize(new Dimension(400, 500)); progressDialog.setSize(300, 100); } @@ -211,6 +220,7 @@ public class MainFrame extends JFrame { pane = new JPanel(); lmsPanel = new LMSPanel(); rmPanel = new RMPanel(); + tsPanel = new TSPanel(); menupanel = new MenuPanel(); northPanel = new JPanel(); @@ -268,6 +278,14 @@ public class MainFrame extends JFrame { } }); + tsPanel.getStartButton().addActionListener((ActionEvent e) -> { + if (tsPanel.getInput() != null){ + Thread t = new Thread( + () -> this.getPresenter().calculateTS(tsPanel.getInput())); + t.start(); + } + }); + importButton.addActionListener((ActionEvent e) -> { SwingUtilities.invokeLater(() -> { File file = null; diff --git a/src/main/java/View/Panels/TSPanel.java b/src/main/java/View/Panels/TSPanel.java new file mode 100644 index 0000000..e9688e5 --- /dev/null +++ b/src/main/java/View/Panels/TSPanel.java @@ -0,0 +1,139 @@ +package View.Panels; + +import View.PlotDialog; +import com.sun.istack.internal.Nullable; +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +/** + * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden. + * + * @Author: Armin Wolf + * @Email: a_wolf28@uni-muenster.de + * @Date: 26.06.2017. + */ +public class TSPanel extends JPanel{ + + private JLabel labels; + private JTextField input; + private JButton startButton; + private JPanel continer; + private JPanel northPanel; + private JPanel centerPanel; + private PlotDialog plotDialog; + private GridBagConstraints gbc; + + public TSPanel() { + this.labels = new JLabel(); + + this.input = new JTextField(); + this.setLayout(new BorderLayout()); + this.northPanel = new JPanel(new BorderLayout()); + this.centerPanel = new JPanel(new BorderLayout()); + this.northPanel.setBorder(new TitledBorder("Konfiguration")); + this.centerPanel.setBorder(new TitledBorder("Visualisierung")); + + this.continer = new JPanel(); + this.continer.setLayout(new GridBagLayout()); + + this.gbc = new GridBagConstraints(); + this.gbc.anchor = GridBagConstraints.NORTH; + this.gbc.fill = GridBagConstraints.HORIZONTAL; + + addTextfieldAndInput(0, "\u00df (0 < \u00df < 1)", 0.5); + + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); + this.startButton = new JButton("Start"); + this.startButton.setFont(new Font("Verdana",Font.PLAIN, 16)); + gbc.insets = new Insets(30, 0, 10, 0); + gbc.gridx = 0; + gbc.gridy = 2; + gbc.weightx = 0.05; + gbc.weighty = 0.05; + buttonPanel.add(startButton); + continer.add(buttonPanel, gbc); + + this.northPanel.add(continer, BorderLayout.CENTER); + this.add(northPanel, BorderLayout.NORTH); + this.add(centerPanel, BorderLayout.CENTER); + } + + + private void addTextfieldAndInput(int row, String name, Double value) { + this.labels = new JLabel(name); + this.labels.setFont(new Font("SansSerif", Font.PLAIN, 13)); + this.input = new JTextField(); + this.input.setText("" + value); + + gbc.insets = new Insets(0, 5, 0, 0); + gbc.gridx = 0; + gbc.gridy = row; + gbc.weightx = 0.05; + gbc.weighty = 0.05; + continer.add(this.labels, gbc); + + gbc.gridx = 1; + gbc.gridy = row; + gbc.weightx = 0.9; + gbc.weighty = 0.05; + gbc.insets = new Insets(0, 0, 0, 5); + continer.add(this.input, gbc); + } + + + public JButton getStartButton() { + return startButton; + } + + @Nullable + public String getInput() { + String input = ""; + input = this.input.getText(); + if (isNumeric(input)) + return input; + else + JOptionPane.showMessageDialog(null, "Bitte geben Sie numerische Werte als Parameter an.","Fehler bei der Eingabe", JOptionPane.ERROR_MESSAGE); + return null; + } + + public void setInput(JTextField input) { + this.input = input; + } + + public PlotDialog getPlotDialog() { + return plotDialog; + } + + public void setPlotDialog(PlotDialog plotDialog) { + this.plotDialog = plotDialog; + if (this.centerPanel.getComponents().length > 0) + this.centerPanel.remove(0); + + this.centerPanel.add(plotDialog, BorderLayout.CENTER); + this.plotDialog.setVisible(true); + this.repaint(); + this.revalidate(); + } + + public boolean isNumeric(String str) { + try{ + double d = Double.parseDouble(str); + } + catch(NumberFormatException nfe){ + return false; + } + return true; + } +} diff --git a/src/test/java/Presenter/Algorithms/LeastMedianOfSquaresEstimatorTest.java b/src/test/java/Presenter/Algorithms/LeastMedianOfSquaresEstimatorTest.java index 402268b..b451d3d 100644 --- a/src/test/java/Presenter/Algorithms/LeastMedianOfSquaresEstimatorTest.java +++ b/src/test/java/Presenter/Algorithms/LeastMedianOfSquaresEstimatorTest.java @@ -8,7 +8,7 @@ import static org.junit.Assert.assertTrue; import Model.Line; import Model.Point; import Model.Slab; -import Presenter.InversionCounter; +import Presenter.IntersectionCounter; import java.util.ArrayList; import java.util.LinkedList; import org.junit.Before; @@ -63,7 +63,7 @@ public class LeastMedianOfSquaresEstimatorTest { for (double d : umax) { b.add((int) d); } - InversionCounter invCounter = new InversionCounter(); + IntersectionCounter invCounter = new IntersectionCounter(); int ret = invCounter.run(a, b); assertEquals(3d, ret, 0.001);