【运筹优化】Java 调用 Cplex 建立混合整数规划模型求解 TSP 问题


本文采用 Miller-Tucker-Zemlin 模型对 TSP 问题进行建模。相关资料可以参考:优化 | 浅谈旅行商(TSP)的七种整数规划模型

一、测试数据

1 6734 1453
2 2233 10
3 5530 1424
4 401 841
5 3082 1644
6 7608 4458
7 7573 3716
8 7265 1268
9 6898 1885
10 1112 2049
11 5468 2606
12 5989 2873
13 4706 2674
14 4612 2035
15 6347 2683
16 6107 669
17 7611 5184
18 7462 3590
19 7732 4723
20 5900 3561

二、完整代码

2.1 算法部分

public class IP_TSP {
    
    
    // 城市坐标<[x,y]>
    List<double[]> locationList;
    // 距离矩阵
    double[][] distance;
    // 城市数量
    int cityNum;
    // 开始地点索引
    int startIndex;

    public IP_TSP(List<double[]> locationList) {
    
    
        this.locationList = locationList;
    }

    public void solve() {
    
    
        initVar();
        solver();
    }

    private void solver() {
    
    
        try {
    
    
            IloCplex cplex = new IloCplex();
            //决策变量
            IloIntVar[][] intVars = new IloIntVar[cityNum][cityNum];
            for (int i = 0; i < cityNum; i++) {
    
    
                for (int j = 0; j < cityNum; j++) {
    
    
                    if (i != j) {
    
    
                        intVars[i][j] = cplex.intVar(0, 1);
                    }
                }
            }
            //目标函数
            IloLinearNumExpr target = cplex.linearNumExpr();
            for (int i = 0; i < cityNum; i++) {
    
    
                for (int j = 0; j < cityNum; j++) {
    
    
                    if (i != j) {
    
    
                        target.addTerm(distance[i][j], intVars[i][j]);
                    }
                }
            }
            //求目标函数的最小值
            cplex.addMinimize(target);
            //约束
            //约束1:每行每列之和等于1
            for (int i = 0; i < cityNum; i++) {
    
    
                IloLinearNumExpr expr_row = cplex.linearNumExpr();
                IloLinearNumExpr expr_col = cplex.linearNumExpr();
                for (int j = 0; j < cityNum; j++) {
    
    
                    if (i != j) {
    
    
                        expr_row.addTerm(1, intVars[i][j]);
                        expr_col.addTerm(1, intVars[j][i]);
                    }
                }
                cplex.addEq(expr_row, 1);
                cplex.addEq(expr_col, 1);
            }
            //约束2:消除子回路
            IloNumVar[] u = cplex.numVarArray(cityNum, 0, Double.MAX_VALUE);
            for (int i = 1; i < cityNum; i++) {
    
    
                for (int j = 1; j < cityNum; j++) {
    
    
                    if (j != i) {
    
    
                        IloLinearNumExpr expr = cplex.linearNumExpr();
                        expr.addTerm(1.0, u[i]);
                        expr.addTerm(-1.0, u[j]);
                        expr.addTerm(cityNum - 1, intVars[i][j]);
                        cplex.addLe(expr, cityNum - 2);
                    }
                }
            }
            //取消cplex输出
            cplex.setOut(null);
            //求解
            if (cplex.solve()) {
    
    
                List<Integer> bestPath = new ArrayList<>();
                bestPath.add(startIndex);
                int index = startIndex;
                while (true) {
    
    
                    for (int i = 0; i < intVars[index].length; i++) {
    
    
                        if (index != i && cplex.getValue(intVars[index][i]) > 1e-06) {
    
    
                            index = i;
                            bestPath.add(i);
                            break;
                        }
                    }
                    if (index == startIndex) {
    
    
                        break;
                    }
                }
                System.out.println("最短路径为:" + bestPath);
                System.out.println("最短路径长度为:" + cplex.getObjValue());
            } else {
    
    
                System.err.println("此题无解");
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

    // 初始化变量
    public void initVar() {
    
    
        // 开始地点索引
        startIndex = 0;
        // 城市数量为点的数量
        cityNum = locationList.size();
        // 距离矩阵
        distance = new double[cityNum][cityNum];
        // 初始化距离矩阵
        for (int i = 0; i < distance.length; i++) {
    
    
            for (int j = i; j < distance[i].length; j++) {
    
    
                if (i == j) {
    
    
                    // 对角线为无穷大
                    distance[i][j] = Double.MAX_VALUE;
                } else {
    
    
                    // 计算i到j的距离
                    distance[i][j] = getDistance(locationList.get(i), locationList.get(j));
                    distance[j][i] = distance[i][j];
                }
            }
        }
    }

    // 计算两点之间的距离(使用伪欧氏距离,可以减少计算量)
    public double getDistance(double[] place1, double[] place2) {
    
    
        // 伪欧氏距离在根号内除以了一个10
//        return Math.sqrt((Math.pow(place1[0] - place2[0], 2) + Math.pow(place1[1] - place2[1], 2)) / 10.0);
        return Math.sqrt((Math.pow(place1[0] - place2[0], 2) + Math.pow(place1[1] - place2[1], 2)));
    }

}

2.2 调用部分

读取数据

    private static List<double[]> getLocationList() throws IOException {
    
    
        FileInputStream fis = new FileInputStream("改成你的数据路径");
        List<double[]> locationList = new ArrayList<>();
        int len = 0;
        StringBuilder sb = new StringBuilder();
        while ((len=fis.read())!=-1){
    
    
            sb.append((char)len);
        }
        fis.close();
        String[] split = sb.toString().split("\n");
        for (String s : split) {
    
    
            String[] s1 = s.split(" ");
            double[] doubles = new double[]{
    
    Double.parseDouble(s1[1]),Double.parseDouble(s1[2])};
            locationList.add(doubles.clone());
        }
        return locationList;
    }

调用算法

    public static void ipTest(List<double[]> locationList) throws Exception{
    
    
        long startTime = System.currentTimeMillis();
        System.out.println("整数规划求解tsp问题:"+locationList.size()+"个城市...");
        new IP_TSP(locationList).solve();
        System.out.println("求解用时:"+(System.currentTimeMillis()-startTime)/1000d+" s");
    }

三、运行结果

整数规划求解tsp问题:20个城市...
最短路径为:[0, 7, 8, 14, 17, 6, 5, 18, 16, 19, 11, 10, 12, 13, 4, 9, 3, 1, 2, 15, 0]
最短路径长度为:22960.870727544883
求解用时:0.369 s

猜你喜欢

转载自blog.csdn.net/weixin_51545953/article/details/126283006
今日推荐