運籌學中的節約里程法及其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=2nC1jz=\sum_{j=2}^n{C_{1j}}
其中C1jC_{1j}爲由點1到點j的路段長度,注意此處假定C1j=Cj1C_{1j}=C_{j1}(對所有j)。

若連接點i和點j,即使旅行者走弧(i,j)時(當然這時就不再經過弧(i,1)和弧(1,j),所引起的路程節約值s(i,j)可計算如下
s(i,j)=2c1i+2c1jc1i+c1j+cij)=c1i+c1jcijs(i,j)=2c_{1i}+2c_{1j}-c_{1i}+c_{1j}+c_{ij})=c_{1i}+c_{1j}-c_{ij}
對不同的點對(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)==c1i+c1jcijs(i,j)==c_{1i}+c_{1j}-c_{ij}
(3)將所有s(i,j)s(i,j)按其值的大小由大到小排列。
(4)按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# 節約里程法實現(原理很詳細,還有例題)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章