用蛮力解决旅行商问题

时间:2019-04-14 14:49:22

标签: java arrays algorithm arraylist

对于一个学校项目,我正在尝试对旅行商问题实施蛮力算法。我想做的是,输入一个代表点数的整数,对于每个点,将给出一个随机的x和y值,该值代表它们在2D平面上的位置。在所有点都初始化之后,该算法将计算从点1开始的每条路线的距离,然后对所有不同的可能性进行置换。

更新1:

因此,我对Point.java和PointManager.java类进行了编码,目的是初始化这些点并将它们保留在arraylist中。 他们看起来像这样 Point.java:

public class Point {

    int x;
    int y;

    //Constructs a random Point
    public Point(){
        this.x = (int)(Math.random()*200);
        this.y = (int)(Math.random()*200);
    }

    //gives Point X Position
    public int getPosX(){
        return this.x;
    }

    //gives Point Y Position
    public int getPosY(){
        return this.y;
    }

    public double distance(Point point){
        int distance_x = Math.abs(getPosX() - point.getPosX());
        int distance_y = Math.abs(getPosY() - point.getPosY());
        double distance = Math.sqrt((distance_x * distance_x) + (distance_y * distance_y));
        return distance;
    }  
}

PointManager.java

import java.util.ArrayList;
public class PointManager {

    // Holds our cities
    private static ArrayList destinationPoints = new ArrayList<Point>();

    // Adds a destination city
    public static void addPoint(Point point) {
        destinationPoints.add(point);
    }

    // Get a city
    public static Point getPoint(int index){
        return (Point)destinationPoints.get(index);
    }

    // Get the number of destination cities
    public static int numberOfPoints(){
        return destinationPoints.size();
    } 
}

现在的问题是,我将如何继续对可能性进行不同的排列,以及如何检查路线中是否已使用了某个点?

1 个答案:

答案 0 :(得分:0)

最直接的解决方案是使用递归:

它跟踪哪些点已访问,哪些仍需要访问。 在每次调用时,它仅使用所有可能的“下一个点”扩展当前路径。

只要没有其他点要包含,它将打印路径。

private static void branch(Set<Point> todoPoints, List<Point> donePoints){
    if(todoPoints.isEmpty()){
        // measure distance
        double totalDistance = 0;
        for (int i = 0; i < donePoints.size(); i++) {
            totalDistance += donePoints.get(i).distance(donePoints.get((i+1) % donePoints.size()));
        }
        System.out.println("Solution found");
        System.out.println("\t" + donePoints);
        System.out.println("\t"+totalDistance);
    }else{
        for(Point nextPoint : todoPoints){
            // add point
            donePoints.add(nextPoint);

            // recurse
            Set<Point> tmp = new HashSet<>(todoPoints);
            tmp.remove(nextPoint);
            branch(tmp, donePoints);

            // remove point
            donePoints.remove(nextPoint);
        }
    }
}

当然可以改进此策略。 我们可以跟踪当前的最小距离,并使用它。 例如,延伸超出当前最小距离的路径没有附加值。

它看起来像这样:

private static void branch2(Set<Point> todoPoints, List<Point> donePoints, double dist){
    if(todoPoints.isEmpty()){
        dist += donePoints.get(0).distance(donePoints.get(donePoints.size()-1));
        CURRENT_MIN_DIST = java.lang.Math.min(CURRENT_MIN_DIST, dist);
        System.out.println("Solution found");
        System.out.println("\t" + donePoints);
        System.out.println("\t"+dist);
    }else{
        for(Point nextPoint : todoPoints){
            // add point
            donePoints.add(nextPoint);

            // update distance
            dist += (donePoints.size() > 1 ? donePoints.get(donePoints.size()-2).distance(donePoints.get(donePoints.size()-1)) : 0);

            // we do not recurse if the current distance is already bigger than a known minimum
            if(dist > CURRENT_MIN_DIST)
                continue;

            // recurse
            Set<Point> tmp = new HashSet<>(todoPoints);
            tmp.remove(nextPoint);
            branch2(tmp, donePoints, dist);

            // remove point
            donePoints.remove(nextPoint);
        }
    }
}

最后,进一步的改进是跟踪完成当前路径所需添加的最小距离。

然后,您可以进一步修剪此递归。 您可以检查

  

当前距离+完成所需的最小距离<当前最小距离

可以这样实现:

private static void branch3(Set<Point> todoPoints, List<Point> donePoints, double dist){
    if(todoPoints.isEmpty()){
        dist += donePoints.get(0).distance(donePoints.get(donePoints.size()-1));
        CURRENT_MIN_DIST = java.lang.Math.min(CURRENT_MIN_DIST, dist);
        System.out.println("Solution found");
        System.out.println("\t" + donePoints);
        System.out.println("\t"+dist);
    }else{
        for(Point nextPoint : todoPoints){
            // add point
            donePoints.add(nextPoint);

            // update distance
            dist += (donePoints.size() > 1 ? donePoints.get(donePoints.size()-2).distance(donePoints.get(donePoints.size()-1)) : 0);

            // we do not recurse if the current distance is already bigger than a known minimum
            if(dist > CURRENT_MIN_DIST)
                continue;

            if(dist + minDistToFinish(todoPoints) > CURRENT_MIN_DIST)
                continue;

            // recurse
            Set<Point> tmp = new HashSet<>(todoPoints);
            tmp.remove(nextPoint);
            branch3(tmp, donePoints, dist);

            // remove point
            donePoints.remove(nextPoint);
        }
    }
}

相关方法如下:

private static double minDistToFinish(Set<Point> todoPoints){
    double distToFinish = 0;
    for(Point p1 : todoPoints){
        double d = Double.MAX_VALUE;
        for(Point p2 : todoPoints){
            if(p1.equals(p2))
                continue;
            d = java.lang.Math.min(d, p1.distance(p2));
        }
        distToFinish += d;
    }
    return distToFinish;
}