运筹学中的节约里程法及其python实现_clarke,wright1964_vivian_ll的博客-程序员秘密

技术标签: 启发式算法  python  运筹学  

节约里程法简介

节约里程法,又称C-W算法 、节约算法或节约法,是由Clarke和Wright于1964年首次提出的,用来解决VRP问题,是重要的物流算法,是用来解决运输车辆数目不确定的问题的最有名的启发式算法。

节约里程法核心思想是依次将运输问题中的两个回路合并为一个回路,每次使合并后的总运输距离减小的幅度最大,直到达到一辆车的装载限制时,再进行下一辆车的优化。优化过程分为并行方式和串行方式两种。

利用节约法确定配送路线的主要出发点是,根据配送中心的运输能力和配送中心到各个用户以及各个用户之间的距离来制定使总的车辆运输的吨公里数最小的配送方案。另还需满足以下条件;(1)所有用户的要求;(2)不使任何一辆车超载;(3)每辆车每天的总运行时间或行驶里程不超过规定的上限;(4)用户到货时间要求。

基本原理

基本思想为为达到高效率的配送,使配送的时间最小距离最短成本最低,而寻找的最佳配送路线。

假定有n个访问地,把每个访问地看成一个点,并取其中的一个点为基点(起点),例如以1点为基点。首先将每个点与该基点相连接,构成线路1→j→1(j=2,3,…,n),这样就得到了一个具有n-1条线路的图(当然,这时尚未形成Hamilton回路)。旅行者按此线路访问这n个点所走的路程总和为
z = ∑ j = 2 n C 1 j z=\sum_{j=2}^n{C_{1j}} z=j=2nC1j
其中 C 1 j C_{1j} C1j为由点1到点j的路段长度,注意此处假定 C 1 j = C j 1 C_{1j}=C_{j1} C1j=Cj1(对所有j)。

若连接点i和点j,即使旅行者走弧(i,j)时(当然这时就不再经过弧(i,1)和弧(1,j),所引起的路程节约值s(i,j)可计算如下
s ( i , j ) = 2 c 1 i + 2 c 1 j − c 1 i + c 1 j + c i j ) = c 1 i + c 1 j − c i j s(i,j)=2c_{1i}+2c_{1j}-c_{1i}+c_{1j}+c_{ij})=c_{1i}+c_{1j}-c_{ij} s(i,j)=2c1i+2c1jc1i+c1j+cij)=c1i+c1jcij
对不同的点对(i,j),s(i,j)越大,旅行者通过弧(i,j)时所节约的路程越多,因而应优先将其安排到旅行线路中去,使旅行者旅行时通过这一条弧。

迭代步骤

在具体应用该方法时,可按以下步骤进行。

(1)选取基点,例如以1点为基点。将每个点与该基点相连接,得到n-1条线路1→j→1(j=2,3,…,n)。
(2)对不违背限制条件的所有可连接点对(i,j),如下计算其节约值(i,j不为基点)
s ( i , j ) = = c 1 i + c 1 j − c i j s(i,j)==c_{1i}+c_{1j}-c_{ij} s(i,j)==c1i+c1jcij
(3)将所有 s ( i , j ) s(i,j) s(i,j)按其值的大小由大到小排列。
(4)按 s ( i , j ) s(i,j) s(i,j)的上述顺序,逐个考察其端点i和j,若满足以下条件,就将弧(i,j)插入到线路中。其条件是:
a. 点i和点j不在一条线路上;
b. 点i和点j均与基点相邻。

(5)返回步骤(4),直至考察完所有可插入弧(i,j)为止。
通过以上各迭代步骤,使问题的解逐步得到改善,最后达到满意解(也有可能达到最优解)。

例题

已知配送中心P0向5个用户Pj配送货物,其配送路线网络、配送中心与用户的距离以及用户之间的距离如下图所示,配送中心有3台2t卡车和2台4t两种车辆可供使用。利用节约里程法制定最优的配送方案。
在这里插入图片描述
第一步,作运输里程表,列出配送中心到用户及用户间的最短距离。
在这里插入图片描述
第二步,按节约里程公式求得相应的节约里程数。
在这里插入图片描述
第三步,将节约里程按从大到小顺序排列。
在这里插入图片描述
第四步,根据载重量约束与节约里程大小,顺序连接各客户结点,形成两个配送线。
P2P3-P3P4-P2P4-P4P5-P1P2-P1P5-P1P3-P2P5-P3P5-P1P4
在这里插入图片描述
得出结果
配送线路一:
运量=1.7+0.9+1.4=4t
运行距离=8+4+5+7=24km
用一辆4t车运送,节约距离为18km
配送线路二:
运量=2.4+1.5=3.9t<4t
运行距离=8+10+16=34km
用一辆4t车运送,节约距离为2km

初始方案:配送线路5条,需要车5辆,配送距离=39*2=78km
优化后的方案:2条配送路线,2辆4t车,配送距离=24+34=58km

python程序

import csv
from operator import itemgetter

class Vrp():

    # -----------初始数据定义---------------------

    def __init__(self):
        self.mans = 9                                                   # 客户数量
        self.tons = 3                                                   # 车辆载重
        self.distanceLimit = 1000                                       # 车辆一次行驶的最大距离
        self.distance = []                                              # 各个客户及配送中心距离
        self.q = [0, 1, 1.1, 0.9, 0.8, 1.1, 0.4, 0.7, 0.8, 0.9]         # 10个客户分布需要的货物的需求量,第0位为配送中心自己
        self.savings = []                                               # 节约度
        self.Routes = []                                                # 路线
        self.Cost = 0                                                   # 总路程

    # -----------导入距离数据---------------------

    def datainput(self):
        with open("data.csv", "r") as csvfile:
            reader = csv.reader(csvfile)
            for line in reader:
                line = [float(x) for x in line]
                self.distance.append(line)

    # -----------节约算法主程序---------------------

    def savingsAlgorithms(self):
        saving = 0
        for i in range(1, len(self.q)):
            self.Routes.append([i])

        for i in range(1, len(self.Routes) + 1):                                                 # 使用Sij = Ci0 + C0j - Cij计算节约度
            for j in range(1, len(self.Routes) + 1):
                if i == j:
                    pass
                else:
                    saving = (self.distance[i][0] + self.distance[0][j]) - self.distance[i][j]
                    self.savings.append([i, j, saving])                                          # 将结果以元组形式存放在列表中

        self.savings = sorted(self.savings, key=itemgetter(2), reverse=True)                     # 按照节约度从大到小进行排序
        for i in range(len(self.savings)):
            print(self.savings[i][0],'--',self.savings[i][1], "  ",self.savings[i][2])           # 打印节约度

        for i in range(len(self.savings)):
            startRoute = []
            endRoute = []
            routeDemand = 0
            for j in range(len(self.Routes)):
                if (self.savings[i][0] == self.Routes[j][-1]):
                    endRoute = self.Routes[j]
                elif (self.savings[i][1] == self.Routes[j][0]):
                    startRoute = self.Routes[j]

                if ((len(startRoute) != 0) and (len(endRoute) != 0)):
                    for k in range(len(startRoute)):
                        routeDemand += self.q[startRoute[k]]
                    for k in range(len(endRoute)):
                        routeDemand += self.q[endRoute[k]]
                    routeDistance = 0
                    routestore = [0]+endRoute+startRoute+[0]
                    for i in range(len(routestore)-1):
                        # print(routestore[i],routestore[i+1])
                        # print(self.distance[routestore[i]][routestore[i+1]])
                        routeDistance += self.distance[routestore[i]][routestore[i+1]]

                    #print(routestore,"== ==:",routeDistance)

                    if (routeDemand <= self.tons) and (routeDistance <= self.distanceLimit):     # 按照限制规则对​​路线进行更改
                        self.Routes.remove(startRoute)
                        self.Routes.remove(endRoute)
                        self.Routes.append(endRoute + startRoute)
                    break

        for i in range(len(self.Routes)):
            self.Routes[i].insert(0, 0)
            self.Routes[i].insert(len(self.Routes[i]), 0)

    # -----------输出最终结果---------------------

    def printRoutes(self):
        for i in self.Routes:
            costs = 0
            for j in range(len(i)-1):
                costs += self.distance[i[j]][i[j+1]]
            print("路线:  ",i,"  路程:  ",costs)

    def calcCosts(self):
        for i in range(len(self.Routes)):
            for j in range(len(self.Routes[i]) - 1):
                self.Cost += self.distance[self.Routes[i][j]][self.Routes[i][j + 1]]

        print("\nTotal Distance: ", round(self.Cost, 3))

    # -----------Master函数---------------------

    def start(self):                      # Master函数,调用所有其他函数
        print("== == == == == == == == == == == == == == == 导入数据 == == == == == == == = == == == == == == == =")
        self.datainput()
        print("== == == 距离表 == == ==")
        for i in self.distance:
            print(i)
        print("== == == 需求表 == == ==")
        print(self.q)
        print("== == == 限制条件 == == ==")
        print("车辆最大载重:",self.tons)
        print("车辆最长运输距离:", self.distanceLimit)
        print("== == == == == == == == == == == == == == == 节约度 == == == == == == == = == == == == == == == =")
        self.savingsAlgorithms()          # 函数调用计算节省量并生成路线
        print("== == == == == == == == == == == == == == == 结果 == == == == == == == = == == == == == == == =")
        self.printRoutes()
        self.calcCosts()
        self.datainput()


if __name__ == '__main__':
	vrp = Vrp()
	vrp.start()

参考网址:
节约里程法-百度百科
节约里程算法的python实现(含github地址)
C# 节约里程法实现(原理很详细,还有例题)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/vivian_ll/article/details/105917188

智能推荐

部署web项目到本地服务器(windows)_前端的小刘老师的博客-程序员秘密

在同一网络下,如果想要让别人能访问到你的项目,或者想用手机访问自己的web项目,那么使用借助Apache将项目部署到本地服务器。在Windows系统中,我们一般借助wampServer(Apache web服务器)进行部署。具体步骤如下:下载wampServer,地址:[wampServer下载地址](https://sourceforge.net/projects/wampserver/)...

adb 常用命令_adb命令查看系统版本_慕涵的博客-程序员秘密

1.连接adbadb connect [ip]:[port] 连接机顶盒(默认端口为5555)2.查看所有连接设备 名称、ip、端口已经状态( device 或 offline )adb devices3.安装apkadb install -r [apk 安装包所在路径(如:d:\a.apk)] 将对应路径的apk 安装包强制(覆盖)安装到机顶盒4.指定设备adb -s [设备名称或...

linux下如何利用nvm安装nodejs_linux nvm node_LaiYoung1022的博客-程序员秘密

1、git安装yum install git2、nvm安装文件获取可通过我的百度网盘链接获取链接:https://pan.baidu.com/s/1XDIyvIEap76Gw07QR8BLuA提取码:0fr0./nvm.sh3、安装nodejs(可选择版本)nvm install v11.6.0

C语言每日一练——第90天:青蛙跳台阶(升级版)_跳台阶问题升级版_Albert Edison的博客-程序员秘密

史上最强青蛙跳台阶问题讲解,代码详解+思路分析+动图演示+特性总结

Flink的批流统一 :Ⅰ_flink 批处理_cuiyaonan2000的博客-程序员秘密

序言Flink的版本号为:1.12 根据最新的版本来研究下Flink的批流统一其实我最想解决的就是Flink能否像Hive 一样来处理大批量数据拆分计算,最后合并。虽然我知道Flink跟MapReduce都是运行于Yarn的,Hive是基于MapReduce来做大批量任务分布式计算的。参考网站:https://ci.apache.org/projects/flink/flink-docs-release-1.12/zh/dev/table/概览Apache Flink 有两..

基于阿里云的C#窗体实现两端通信_用c#窗体程序读取阿里云物联网的设备数据_simplebooy的博客-程序员秘密

基于阿里云的C#窗体实现两端通信来自一位大手子@Eragonl,大家可以在csdn找到他前期准备·1.1所需工具的确认和安装:打开VisualStdio Installer后点击上方的单个组件后向下寻找到 .NET Framework的SDK和目标包,确认好至少安装了一个版本的SDK和目标包(我这里使用的是4.6.1的版本)​ ·1.2 创建一个C#窗体:·1.3 下载MQTT库和必要的SDK文件创建完成以后,现在我们来下载MQTT库的4.3.0版本。首先我们需要在VS

随便推点

java实现简单万年历_Colin丶c的博客-程序员秘密

思路:1、算出当前月份的第一天与1900/1/1 的天数之差 day 2、将day%7得到当前月的第一天是星期几3、循环打印日历代码实现:import java.util.Calendar;import java.util.Scanner;public class Test { public static void main(String[] args) { ...

zabbix3.4.2的安装及配置_无知的蜗牛的博客-程序员秘密

zabbix3.4.2的安装及配置是建立在lnmp环境搭建的基础上的,如果对lnmp环境有疑问请移步至:http://blog.csdn.net/weixin_37998647/article/details/78832562一、下载编译安装 1.1下载源码包wget -O zabbix-3.4.2.tar.gz http://sourceforge.net/projects/zabbix/fil

x64驱动强制读写功能之[驱动取模块基址]技术_易语言 驱动读写模块_hzw320的博客-程序员秘密

现在很多比较热门新出的游戏,都具备了一些反辅助保护的机制,比如防止游戏内存数据被第三方软件程序(辅助)读写所以我重新开发了下我们Game-EC 驱动模块8.5.5加密狗模块版本里的驱动,并且支持到了win7,win8,win10 全64位系统,让大家使用我们模块开发辅助更效率更轻松面对各种游戏问题。类_x64强制读写 教程:链接:百度网盘 请输入提取码提取码:fhy7bilibili:易语言x64驱动强制读写-取进程模块基址_哔哩哔哩_bilibili...

主流RPC框架详解,以及与SOA、REST的区别_大G哥的博客-程序员秘密

什么是RPCRPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。简言之,RPC使得程序能够像访问本地系统资源一样,去访问远端系统资源。比较关键的一些方面包括:通讯协议序列化资源(接口)描述服务框架性能语言支持等。REST 和 SOAP、RPC1.REST可以看着是http协议的一种直...

php 7.2 aes 128 ECB 加密_qq_21761149的博客-程序员秘密

欢迎大家访问我的博客www.kevink.clubphp7.1 开始弃用mcrypt 加密 改为使用 openssl 加密以下列出 aes ecb 加密 5.* 和 7.2及以上加密方式php5.*```$key = 'Op2TlBNJ3drx71rF';$string = '{"channel":"djqm","productId":"I19BR9","tranCode":"SPE20200804995150100991","userName":"陈先生","sex":"101120.

如何优化cocos2d/x程序的内存使用和程序大小_weixin_30917213的博客-程序员秘密

在我完成第一个游戏项目的时候,我深切地意识到“使用cocos2d来制作游戏的开发者们,他们大多会被cocos2d的内存问题所困扰”。而我刚开始接触cocos2d的时候,社区里面的人们讨论了一个非常有意义的话题:“请简单地讲述你认为新手cocos2d程序员在他开始编码之前,最应该先知道,或者应该关注和注意的事项。”这个问题的答案很多,有人讲是“如何加载和保存游戏数据”,有人讲的是“如何实...

推荐文章

热门文章

相关标签