数学模型——垃圾运输问题,运输车的路径以及数量选择(含matlab代码)

本文详细介绍了如何利用遍历和迭代策略解决一个垃圾运输的NP完全问题,通过建立数学模型和约束条件,寻找运输车的最优路线和最低费用。作者提供了Matlab代码实现,展示了如何在限制时间内找到多条最优路线,并计算总费用和时间成本。
摘要由CSDN通过智能技术生成

写在前面

垃圾运输问题可以看作是一个NP完全问题,但很遗憾的是,目前我们所掌握的数学知识并不足以让我们找到一个有效找到NP完全问题全局最优解的算法,甚至,在数学界中还无法确定NP完全问题是否真的存在找到全局最优解的有效算法。在本问题中,我选择通过遍历和迭代来寻找最优解,当然,我会给出一些约束条件来对其做出限制,以此来限制遍历的方向,加快遍历的速度。

问题

其实这个作业一共有3问,但本文着重介绍第1个问题的解决方法,因此只将第1个问题截图出来。

在本问题中,我们可以知道:

  • 每个垃圾站的编号,横坐标,纵坐标以及垃圾量
  • 运输车的装载量为6t
  • 运输车的速度为40km/h
  • 运输车只能工作4h
  • 运输车空载时费用为0.4元/公里
  • 运输车运载时的费用为1.8元/吨公里
  • 运输车每次装垃圾需要花费10min,即1/6h

问:需要多少辆运输车,每辆运输车的路径以及总费用

问题分析

从问题中,我们得出了许多关于运输车的约束条件:

运输车的装载量仅有6t,这就约束了运输车在选点规划路线时,经过的路线垃圾总量必须小于6t,一旦装完第n个点,在找n+1个点时,发现地图上剩余点的垃圾量都超过了运输车还剩下的装载量,则垃圾车必须返回垃圾处理厂,即(0,0)点。

运输车只能工作4h,也就是说,虽然运输车的装载量约束了一辆车一趟仅能装多少垃圾,但是如果装完回来卸货后,如有剩余时间,则可以继续出发。

运输车的速度以及装垃圾所花费的时间都是约束运输车的工作时间。

运输车的运载费用分为两部分,一部分是空载时,0.4元/公里。另一部分是装有垃圾时,1.8元/吨公里。也就是说,空载时花费的费用最少,一旦装有垃圾,则装的越多,花费越多,例如:当我经过第1点时(假设我已经知道最优路线),我需要将第1点的垃圾装入,则,我从第1点到第2点之间的花费为:
                        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        Z=1.8\times C_{1}\times D_{1\rightarrow 2}

其中:C_{1}表示第1个点的垃圾量(吨),D_{1\rightarrow 2}表示第1点到第2点之间的距离。

因为垃圾量随着经过的站点增加而增加,因此在路途中的花费几乎是越来越大。如果是第n个点到第n+1个点之间的花费,则将上式中的“1”改为“n”,“2”改为“n+1”即可。

因此,我们可以约定:让费用越低的状态走越长的路,让费用越高的状态走越短的路,在路线中,最后的点应该最接近垃圾处理厂(0,0),最初的点应该是路线中最远离垃圾处理厂(0,0)。这样一条最优路线中费用就能最大限度地降低。

模型建立

我们可以建立这样的模型:

        最小运输费的目标函数为:

                               min Z=0.4\times C_{i}\times D_{0i}+1.8\times \sum_{k=i}^{l}(\sum_{j=i}^{k}C_{j})\times D_{kl}

其中,这里的下标表示点的编号,而非路线的顺序,例如:0i表示从原点到编号为 i 的点,即:D_{0i}表示从原点到编号为 i 点的距离。C{_{i}}表示第 i 点的站的垃圾量

需要注意的是:在本式中 k 并不是顺序数列,而是需要通过约束原则选取出来的无序点的集合

通过对问题的分析,我们可以给出以下的约束条件

  • 路线的第1个点应该是距离原点最远的点(我们需要空载状态下走最远,而不能让装着垃圾走最远,若装垃圾跑最远,返程时消耗的费用要比空载大)
  • 当前点与原点的距离应该比下一点与原点的距离要大,(我们必须先跑到最远点,然后往回运垃圾)
  • 当前点与下一点的距离应该是所有可选点中最短的(一样的原因,我们不能装着垃圾跑太远)
  • 下一点的垃圾量应该小于运输车剩余装载量
  • 下一点为可选点,即下一点尚未经过,垃圾未被装走

                                                         s.t\left\{\begin{matrix} \\ max D_{0\rightarrow i} \ \ \ \ \ \ \ \ \ \ \ H_{i}=0 \\ H_{k}=(0,1) \ \ \ \ \ k=(1..36) \\ min D{_{j\rightarrow k}} \ \ \ \ \ \ \ \ \ \ \ \ H_{k}=0 \\ D_{0\rightarrow j}>D_{0\rightarrow k} \ \ \ \ \ H_{j}=1 \end{matrix}\right.

其中,H_{i}表示第 i 站是否已经被选择过了,0表示还未选择,1表示已经被选择过了。D已经在上面阐述过,此处不再赘述

满足以上条件的点,则是我们选择的点,当一条路线中,其余可选点都不满足条件,则该路线的车之间从路线的最后一个点上返回原点即可。

可以看到,我并没有对时间进行约束,这是因为,在数据预处理时,我粗略地计算了运输车在规定时间内能经过很多点,但装载量却不能承受特别多的点,因此装载量的对路线的约束很大,而时间对路线的约束可以忽略。

这样,我们就确定了一条最优路线,当然,在题目中,一条最优路线是不能经过全部的点的,所以我们需要不断遍历:

  1. 通过上述的约束条件找到最优路线
  2. 将路线中经过的点标记为不可选择
  3. 判断是否还剩可选择的点,若有,则返回1,若无则说明全部点都遍历完,退出遍历

这样,我们就能找到多条最优路线,将所有的点都按照约束条件遍历完成,只需要在编程时,一旦选择一点,则将该点的编号记录保存在路线矩阵中,每一条路线的点的编号都记录下来,只需要算出路线的费用,就是最小费用,计算出路线的时间,判断车辆能否通过,若能通过,还剩多少时间,能否再走一条路,这样我们就能选择派出多少辆车。 

Matlab代码

clear, clc, close

%% 生成数据 
%x,y表示垃圾集中点坐标,m表示垃圾量,N表示坐标
x=[3 1 5 4 0 3 7 9 10 14 17 14 12 10 7 2 6 11 15 19 22 21 27 15 15 20 21 24 25 28 5 17 25 9 9 30 0];
y=[2 5 4 7 8 11 9 6 2 0 3 6 9 12 14 16 18 17 12 9 5 0 9 19 14 17 13 20 16 18 12 16 7 20 20 12 0];
t=[1.5 1.5 0.55 1.2 0.85 1.3 1.2 2.3 1.4 1.5 1.1 2.7 1.8 1.8 0.6 1.5 0.8 1.5 0.8 1.4 1.2 1.8 1.4 1.6 1.6 1.0 2.0 ...
    1.0 2.1 1.2 1.9 1.3 1.6 1.2 1.5 1.3 0.0];
a = 1:37;
N = 1:37;

    dots=[N;x;y;t;a];%N为编号,x为横坐标,y为纵坐标,t为垃圾量,a为0-1变量,0表示该点尚未经过,1表示该点已经经过了
%画图
plot(dots(2,:),dots(3,:),'r*')

dots(5,:)=0;
jg = zeros(12,12);  %选择路线矩阵,(i,j)=k 表示第i条路线的第j个经过的点为编号 k 的点

%% 寻找最优路线
for i=1:20%遍历20次,寻找20条路线
    sum=0;
    j1=1;
    s=0;
    m=37;
    i3=37;
    
    for j=1:36%第一次循环,找初始点,即最远的节点
        if(sqrt(dots(2,j)^2+dots(3,j)^2)>s&&dots(5,j)==0) %满足:1.该点是所有未经过点中的最远点 2.该点并未经过  则:
            s=sqrt(dots(2,j)^2+dots(3,j)^2);%记录该点到起始点的距离
            jg(i,j1)=dots(1,j);%将该点记录为第i条路线的第1个点
            sum=dots(4,j);%垃圾量
            m=j;%找到最远的节点,并把标号赋值给m
        else continue;%若不满足条件,则进入下一次循环,判断下一个点
        end
    end
    dots(5,m)=1;%最远的节点更改为已选取的状态
    j1=j1+1;
    while 1
        js=0;%初始化纳入路线的点的判断
        q=1000;%初始化点间距离
        for k=1:36
            %若满足以下条件则进入if:
            %1.此点与上一点之间距离比其他点与上一点的距离更短
            %2.距离原点的距离小于上一点(防止拖着更多垃圾跑得更远,费用更多)
            %3.该点垃圾量小于剩余装载量
            %4.该点尚未经过
            if(q>sqrt((dots(2,m)-dots(2,k))^2+(dots(3,m)-dots(3,k))^2)&&(dots(2,m)^2+dots(3,m)^2)>(dots(2,k)^2+dots(3,k)^2)&&(6-sum)>dots(4,k)&&dots(5,k)==0)
                q=sqrt((dots(2,m)-dots(2,k))^2+(dots(3,m)-dots(3,k))^2);%两点之间的距离
                js=1;%标记为存在符合条件的点
                jg(i,j1)=dots(1,k);%将该点编号放入第i条路线的第j1个点
                i3=k;%记录编号
            else continue;%如果此点不符合条件,则判断下一个点
            end
        end
            dots(5,i3)=1;%将此点标记为已经过
            sum=sum+dots(4,i3);      %第i条路线目前为止的垃圾总量
            j1=j1+1;%该路线增加一个点
            m=i3;%将该点标为上一点,进入下一次循环
            if(dots(2,i3)==0&&dots(3,i3)==0||js==0)%若没有任何点满足条件,即唯一符合条件的只有上一个点本身,则说明该路线以达到最优,该路线规划完成,退出循环
                break;
            end
    end
end
%% 计算费用
    kcost=0;%第1个点的花费
    zcost=0;%除第1个点外其它点的总花费
    allcost=0;%总花费
    n=0;%初始化编号
    nl=0;%初始化第1个点的编号
    nn=0;%初始化
    sum=0;%初始化垃圾装载量
    %路线矩阵遍历:第u1路线的第u2个点
    for u1=1:12
        sum=0;
        nl=jg(u1,1);%获取第1个点的编号
        if(nl~=0)%若编号不为0.则说明路线存在
            kcost=kcost+0.4*sqrt(dots(2,nl)^2+dots(3,nl)^2);%计算当前路线的第1个点的花费
        else continue%若不存在该路线,则进入下一次循环
        end
        
        for u2=2:12
            if jg(u1,u2)~=0%若此点不为0,则该路线还未走完
                nn=jg(u1,u2);%获取第u1路线的第u2点的编号
            else continue;%若路线走完了,则进入下一条路线
            end
            sum=sum+dots(4,nl);%记录经过当前垃圾站的垃圾吨数
            zcost=zcost+sum*1.8*sqrt((dots(2,nn)-dots(2,nl))^2+(dots(3,nn)-dots(3,nl))^2);%计算除第1个点外其它点的花费
            nl=nn;%将当前点作为上一节点
        end
        sum=sum+dots(4,nl);
        zcost=zcost+sum*1.8*sqrt(dots(2,nl)^2+dots(3,nl)^2);%返回垃圾处理场的花费

    end
    allcost=zcost+kcost;%总花费
    zcost
    kcost
    %% 计算路线所花费的时间
    i=1:12; 
    time=[i];%建立时间矩阵
    time(1,:)=0;%初始化时间矩阵
    n1=0;
    n2=0;
    n3=0;
    for i=1:12
        if(jg(i,1)~=0)
            n3=jg(i,1);
            time(1,i)=sqrt(dots(2,n3)^2+dots(3,n3)^2)/40+(1/6); % 第i条路线,从原点到第1个点的时间花费
            n2=n2+1;
        else continue
        end
        for j=2:12
            if(jg(i,j)~=0)
                n1=jg(i,j);
                n2=n2+1;
            else continue
            end 
            time(1,i)=time(1,i)+sqrt((dots(2,n1)-dots(2,n3))^2+(dots(3,n1)-dots(3,n3))^2)/40+(1/6); %第i条路线,当前点到下一个点的时间花费
            n3=n1;%令n1的点为当前点
        end
            time(1,i)=time(1,i)+sqrt(dots(2,n3)^2+dots(3,n3)^2)/40;%返回垃圾处理厂的时间

    end

代码分析:

代码前面输入点的信息将其存储起来,我将点的信息画了个图表示出来

 

代码核心分为三个部分:

第一部分是选择最优路线,在该部分中,按照模型建立时所说的约束条件来进行遍历,距离取两点之间的线段作为距离,假设运输车不发生转弯等情况。一共遍历20次,这个值可以自己定,但尽量别太小,因为太小时可能走不完所有的点。

jg为路线矩阵,最终路线矩阵数据如下:

 该图中,第1条路线为:37-30-29-27-15-37,因0表示没有点,因此我用37号点表示垃圾处理厂(0,0),其它点同理。

第二部分是根据路线矩阵,计算运输费用:

计算得:

 第三部分是根据路线矩阵,计算每条路线的时间花费:

 可以看到,没有一条路线的时间超过运输车工作的4h,所以每条路线都是可行路线,其实在一开始进行数据预处理时就粗略地计算了以下,运输车的时间很充裕,对其约束性很小,关键是运输车的载量,对路线的约束更大。因此我们在讨论约束条件时只讨论了载量限制,现在才讨论时间限制。

根据计算,第9条路线与第10条路线时间相加小于4h,其余路线不可相加,因此我们需要9辆运输车,因为运输车的启动并不需要消耗费用,因此,我想选9辆车还是10辆车出发都可以,嘿嘿。

结语

至此,该问题就解决啦。如果你看到这里,非常感谢!也希望我的博客能够帮助到你。

本人是一名大三学生,文章质量还是解法都不是最优的,也许我解出来的并不是最优解,还有大神能够解出费用比我更低,希望能够一起讨论,若文章中有什么看不懂或错误地方,请批评指正!

  • 26
    点赞
  • 117
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

苏三有春

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

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

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

打赏作者

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

抵扣说明:

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

余额充值