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

本文详细介绍了如何运用Miller-Tucker-Zemlin模型来解决旅行商问题(TSP),并提供了完整的Java代码实现,包括算法部分和调用部分。代码利用Cplex库求解最短路径,给出了20个城市的测试数据和运行结果,展示了解决方案的最短路径和耗时。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


本文采用 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WSKH0929

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值