【运筹优化】运输问题建模 + Java调用Cplex求解


一、问题描述

运输问题(transportation problem)一般是研究把某种商品从若干个产地运至若干个销地而使总运费最小的一类问题。

本博客将根据下面的例题,介绍运输问题的建模和求解

在这里插入图片描述


二、思路分析

在料场与工地之间计算一个距离弧,然后将分配到弧上的运输量作为决策变量,从而建立线性规划模型调用Cplex求解。


三、建模方案

x i j x_{ij} xij为料场 i = ( 1 , 2 , . . . , n ) i=(1,2,...,n) i=(1,2,...,n)向工地 j = ( 1 , 2 , . . . , m ) j=(1,2,...,m) j=(1,2,...,m)运输的水泥量(吨), d i j d_{ij} dij为料场 i i i到工地 j j j的欧几里得距离(km), c i c_i ci为料场 i i i的日储量(吨), s j s_j sj为工地 j j j的日用量(吨),则问题(1)可以被建模为如下的线性规划模型:

在这里插入图片描述


四、Java调用Cplex代码

import ilog.concert.IloLinearNumExpr;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;
import lombok.AllArgsConstructor;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author:WSKH
 * @ClassName:Answer1
 * @Description:
 * @Time:2023/8/15/9:49
 * @Email:1187560563@qq.com
 * @Blog:wskh0929.blog.csdn.net
 */
public class Answer1 {
    /**
     * 料场对象
     */
    @AllArgsConstructor
    static class Stockyard {
        /**
         * x,y坐标和储量c
         */
        double x, y, c;
    }

    /**
     * 工地对象
     */
    @AllArgsConstructor
    static class ConstructionSite {
        /**
         * x,y坐标和需求d
         */
        double x, y, d;
    }

    private static double calcDistance(double x1, double y1, double x2, double y2) {
        return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
    }

    public static void main(String[] args) throws Exception {
        // 浮点型精度误差
        double EPS = 1e-06;
        // 料场列表
        List<Stockyard> stockyardList = new ArrayList<>();
        stockyardList.add(new Stockyard(5, 1, 20));
        stockyardList.add(new Stockyard(2, 7, 20));
        // 工地列表
        List<ConstructionSite> constructionSiteList = new ArrayList<>();
        constructionSiteList.add(new ConstructionSite(1.25, 1.25, 3));
        constructionSiteList.add(new ConstructionSite(8.75, 0.75, 5));
        constructionSiteList.add(new ConstructionSite(0.5, 4.75, 4));
        constructionSiteList.add(new ConstructionSite(5.75, 5, 7));
        constructionSiteList.add(new ConstructionSite(3, 6.5, 6));
        constructionSiteList.add(new ConstructionSite(7.25, 7.75, 11));
        // 计算距离矩阵
        double[][] distanceMatrix = new double[stockyardList.size()][constructionSiteList.size()];
        for (int i = 0; i < distanceMatrix.length; i++) {
            Stockyard stockyard = stockyardList.get(i);
            for (int j = 0; j < distanceMatrix[i].length; j++) {
                ConstructionSite constructionSite = constructionSiteList.get(j);
                distanceMatrix[i][j] = calcDistance(stockyard.x, stockyard.y, constructionSite.x, constructionSite.y);
            }
        }
        // 开始建模
        IloCplex cplex = new IloCplex();
        // 声明变量
        IloNumVar[][] x = new IloNumVar[stockyardList.size()][constructionSiteList.size()];
        for (int i = 0; i < x.length; i++) {
            for (int j = 0; j < x[i].length; j++) {
                x[i][j] = cplex.numVar(0, Math.min(stockyardList.get(i).c, constructionSiteList.get(j).d));
            }
        }
        // 构造约束1:必须满足每个工地的需求
        for (int j = 0; j < constructionSiteList.size(); j++) {
            IloLinearNumExpr expr = cplex.linearNumExpr();
            for (int i = 0; i < x.length; i++) {
                expr.addTerm(1, x[i][j]);
            }
            cplex.addEq(expr, constructionSiteList.get(j).d);
        }
        // 构造约束2:不能超过每个料场的储量
        for (int i = 0; i < stockyardList.size(); i++) {
            cplex.addLe(cplex.sum(x[i]), stockyardList.get(i).c);
        }
        // 声明目标函数
        IloLinearNumExpr target = cplex.linearNumExpr();
        for (int i = 0; i < x.length; i++) {
            for (int j = 0; j < x[i].length; j++) {
                target.addTerm(distanceMatrix[i][j], x[i][j]);
            }
        }
        cplex.addMinimize(target);
        // 配置cplex
        cplex.setOut(null);
        cplex.setWarning(null);
        cplex.setParam(IloCplex.DoubleParam.EpOpt, EPS);
        // 开始求解
        long s = System.currentTimeMillis();
        if (cplex.solve()) {
            System.out.println("最小吨千米数为: " + cplex.getObjValue());
            for (int i = 0; i < stockyardList.size(); i++) {
                for (int j = 0; j < x[i].length; j++) {
                    double xValue = cplex.getValue(x[i][j]);
                    if (xValue > EPS) {
                        System.out.println("料场" + (i + 1) + "向工地" + (j + 1) + "运输" + xValue + "吨水泥");
                    }
                }
            }
            System.out.println("求解用时: " + (System.currentTimeMillis() - s) / 1000d + " s");
        } else {
            System.err.println("此题无解");
        }
        // 结束模型
        cplex.end();
    }
}

五、输出结果

最小吨千米数为: 136.22751988318154
料场1向工地1运输3.0吨水泥
料场1向工地2运输5.0吨水泥
料场1向工地4运输7.0吨水泥
料场1向工地6运输1.0吨水泥
料场2向工地3运输4.0吨水泥
料场2向工地5运输6.0吨水泥
料场2向工地6运输10.0吨水泥
求解用时: 0.002 s
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WSKH0929

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

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

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

打赏作者

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

抵扣说明:

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

余额充值