【问题标题】:Travelling Salesman visualisation in Java (Swing)Java 中的旅行推销员可视化(Swing)
【发布时间】:2015-06-21 13:32:31
【问题描述】:

出于练习目的,我挑战自己编写了一个解决 TSP 并逐步可视化结果的程序。

就目前而言,我的程序使用简单的最近邻算法。我希望我的程序灵活一些,所以当我添加一个新算法时,它也能够将结果可视化,而不会弄乱显示的逻辑。

我遇到的一个问题是 - 如何逐步显示解决方案?我通过创建多个部分解决方案、存储它们并一个接一个地显示来解决它。感觉还可以做的更好,但是我的图形不太好,希望能在这里得到一些线索。

下面是一些代码: Point 类- 代表一个城市。

class Point {
    private double x;
    private double y;

    public double getX() {
        return x;
    }
    public double getY() {
        return y;
    }

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public Point(){
        Random r = new Random();
        x=r.nextInt(1000);
        y=r.nextInt(650);
    }

    public double calculateDistanceToPoint(Point p) {
        double dist = Math.sqrt(Math.pow(this.x-p.x, 2) + Math.pow(this.y-p.y, 2));
        return round(dist,2);
    }

    private static double round(double value, int places) {
        if (places < 0) throw new IllegalArgumentException();

        BigDecimal bd = new BigDecimal(value);
        bd = bd.setScale(places, RoundingMode.HALF_UP);
        return bd.doubleValue();
    }
}

然后,Solver 类正在计算:

class Solver {
    //list of all points to visit
    private static ArrayList<Point> points = new ArrayList<>();

    //adjacency matrix
    private ArrayList<ArrayList<Double>> adjMatrix = new ArrayList<>();

    //found solution
    private static ArrayList<Point> solution = new ArrayList<>();

    //visited points
    private ArrayList<Integer> visitedPoints = new ArrayList<>();

    //used for visualisation
    private static Solution finalSolution = new Solution();

    public void clear() {
        points.clear();
        solution.clear();
        visitedPoints.clear();
        adjMatrix.clear();
        finalSolution.clear();
    }

    public void addPoint(Point p) {
        points.add(p);
    }

    public static ArrayList<Point> getPoints() {
        return Solver.points;
    }

    public void fillAdjacencyMatrix() {
        int iter_x;
        int iter_y;
        for (iter_x = 0; iter_x < points.size(); iter_x++) {
            ArrayList<Double> temp = new ArrayList<>();
            for (iter_y = 0; iter_y < points.size(); iter_y++) {
                if (iter_x == iter_y) {
                    temp.add(-1.0);
                } else {
                    temp.add(points.get(iter_x).calculateDistanceToPoint(points.get(iter_y)));
                }
            }
            adjMatrix.add(temp);
        }
    }

    private int getIndexOfMin(ArrayList<Double> arr) {
        Double min = Double.MAX_VALUE;
        int index = -2;
        for (int i = 0; i < arr.size(); i++) {
            Double val = arr.get(i);
            if (!(val == -1.0) && !visitedPoints.contains(i) && val < min) {
                min = val;
                index = i;
            }
        }
        return index;
    }

    public void solveUsingNN(int startingPoint) {
        int noOfVisited = 0;

        //find nearest point from the starting one
        int nearest = getIndexOfMin(adjMatrix.get(startingPoint));
        Solution sol = new Solution();

        //until we've visited all points
        while (noOfVisited!=points.size()) {
            //get next nearest point and add it to visited
            nearest = getIndexOfMin(adjMatrix.get(nearest));
            visitedPoints.add(nearest);

            //add this point to solution
            Point newPoint = points.get(nearest);
            solution.add(newPoint);

            //create a new frame for animation, containing all previous steps and recently added one
            SolutionStep ss = new SolutionStep();
            Point p;
            for (Point newPoint : solution) {
                p = new Point(newPoint.getX(), newPoint.getY());
                ss.addPoint(p);
            }
            sol.addStep(ss);
            noOfVisited++;
        }
        finalSolution=sol;
    }
}

然后,SolutionStep 类:

class SolutionStep{
    public final ArrayList<Point> step = new ArrayList<>();
    public SolutionStep(){}

    public void addPoint(Point p){
        step.add(p);
    }
    public void draw(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
            for (int i = 0; i < step.size()-1; i++) {
                g2.draw(new Line2D.Double(step.get(i).getX(), step.get(i).getY(), step.get(i + 1).getX(), step.get(i + 1).getY()));
        }
    }
}

还有Solution,其中包含很多步骤。

public class Solution {
    private ArrayList<Point> points = new ArrayList<>();
    private static ArrayList<SolutionStep> playbackSolution = new ArrayList<>();
    private int noOfFrames;

    public Solution(ArrayList<SolutionStep> listOfSteps, int noOfFrames){
        this.noOfFrames=noOfFrames;
        playbackSolution=listOfSteps;
    }
    public Solution(){}

    public static ArrayList<SolutionStep> getPlayback(){
        return playbackSolution;
    }
    public void clear(){
        playbackSolution.clear();
    }

    public void addStep(SolutionStep solutionStep){
        playbackSolution.add(solutionStep);
    }

    public void draw(Graphics g) {
        int numberOfPoints;
        points = Solver.getPoints();
        Graphics2D g2 = (Graphics2D) g;
        //draw all points
        for (Point point : points) {
            g2.fill(new Rectangle2D.Double(point.getX(), point.getY(), 6, 6));
        }

        //draw next line
        for(int i = 0;i<noOfFrames;i++) {
            playbackSolution.get(i).draw(g);
        }

        //if we are at the final solution, draw a line from last point to the first
        if (noOfFrames == points.size()){
            numberOfPoints = points.size();
            Point first = playbackSolution.get(0).step.get(0);
            Point last = playbackSolution.get(numberOfPoints-1).step.get(numberOfPoints-1);
            g2.draw(new Line2D.Double(first.getX(), first.getY(), last.getX(), last.getY()));
        }
    }
}

最后,Visualisation

class Visualisation extends JFrame {
    private DrawingPanel contentPane;
    private int noOfPoints = 10;
    private int delay_time = 300;

    public Visualisation() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 1100, 700);
        contentPane = new DrawingPanel();
        setContentPane(contentPane);
        JButton start = new JButton("Start");
        start.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                Solver s = new Solver();
                s.clear();
                contentPane.displayNoOfSteps = 0;
                for (int i=0;i<noOfPoints;i++) {
                    s.addPoint(new Point());
                }
                s.fillAdjacencyMatrix();
                s.solveUsingNN(0);
                new javax.swing.Timer(delay_time, new ActionListener(){
                    @Override
                    public void actionPerformed(ActionEvent e){
                            contentPane.repaint();
                    }
                }).start();
                contentPane.repaint();
            }
        });
        contentPane.add(start);
    }
}

还有DrawingPanel:

class DrawingPanel extends JPanel{
    public int displayNoOfSteps = 1;

    DrawingPanel(){}
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Solution sol = new Solution(Solution.getPlayback(),displayNoOfSteps);
        sol.draw(g);

        if (displayNoOfSteps< Solution.getPlayback().size())
        displayNoOfSteps++;
        }
    }

主类:

class Main {
    public static void main(String[] args){
        Visualisation frame = new Visualisation();
        frame.setVisible(true);
    }
} 
  1. 现在,在Visualisation 类中,我有一个Timer。此计时器每隔delay_time ms 在DrawingPanel 上调用repaint(),并且在每次迭代中它都会增加要显示的步数。问题是,如果我运行一个模拟,然后再次点击Start,模拟运行得更快,并且经过几次运行后,它几乎立即显示最后一步。我不知道出了什么问题。我该如何处理?

  2. 程序启动时出现错误-

    at Solution.draw(Solution.java:57)

    at DrawingPanel.paintComponent(Visualisation.java:53)

指的是行:

playbackSolution.get(i).draw(g);

sol.draw(g);

但我还没有画任何东西! repaint()ActionListener 中,在 JButton 中。或者也许画一个JButton 调用draw() 无论如何?我怎样才能摆脱这个问题?

  1. 另外,我觉得我使用了太多静态字段和方法 - 但另一方面,创建 Solver 的实例然后使用非静态方法会更好吗获得解决方案?或者也许让Solver 成为单身人士?反正有一个实例。

  2. 正如我前面提到的,我想在编写更复杂的算法(例如模拟退火)之前获得有关此代码的一些反馈,这样很容易保持良好的代码质量。我可以在此代码中进行哪些更改以更轻松地添加新功能?

【问题讨论】:

  • 1) 为了尽快获得更好的帮助,请发布MCVE(最小完整可验证示例)或SSCCE(简短、自包含、正确示例)。 2) 始终复制/粘贴错误和异常输出(整个堆栈跟踪)!
  • 顺便说一句 - 这个“问题”实际上包含 4 个独立的问题,应该分成 4 个独立的、自包含的消息线程。 SO 是一个问答网站,而不是“一站式修复我的代码”商店。
  • 谢谢,我会记住这一点并发布单独的后续问题。
  • 嗯,我已经投票关闭了这个,你可以随时edit it

标签: java algorithm swing


【解决方案1】:

拥有维护一个相当大的 Java Swing 应用程序的经验,我永远不会自愿承诺在 Swing 中做一些新的事情。

我认为通过使用第三方工具来可视化图表可以很好地解决这个特殊问题。 Graphviz 是一个选项,但还有其他几个工具。更多示例请查看here

您所做的只是以可视化工具的符号生成图表。您可以使用节点名称来显示 Salesman 采用的路径:1、2、3 等。

【讨论】:

  • 它从来就不是一个真正的大型应用程序,只是用来显示节点和边缘的东西。我会看看 Graphviz,谢谢。
猜你喜欢
  • 2015-04-28
  • 1970-01-01
  • 2013-06-09
  • 2011-09-08
  • 1970-01-01
  • 2014-10-24
  • 2017-11-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多