蟻群算法及其python實(shí)現(xiàn)(針對TSP問題)

1.定義

蟻群算法(Ant Colony Optimization, ACO)是由Marco Dorigo于1992年在他的博士論文“Ant system: optimization by a colony of cooperating agents”中提出积蜻,其靈感來源于螞蟻在尋找食物過程中發(fā)現(xiàn)路徑的行為芦岂。

2.原理簡述

??螞蟻在尋找食物或者運(yùn)動(dòng)過程中,會在移動(dòng)的行進(jìn)的路徑上留下一種稱之為信息素(pheromone)的物質(zhì)橙凳。該物質(zhì)有兩種比較重要的特性:

  • 會隨著時(shí)間逐漸揮發(fā)茧吊;
  • 信息素對螞蟻有吸引力豆村。

而螞蟻在選擇路徑的時(shí)候也會有一定的特性:

  • 螞蟻往往傾向于選擇信息素濃度更高的方向行進(jìn),但也會有螞蟻特立獨(dú)行缨历;
  • 在沒有信息素作為參考的地方猩谊,螞蟻一般隨機(jī)選擇行進(jìn)方向千劈,并帶有一定慣性。

因此受信息素機(jī)制的影響牌捷,并在正反饋的作用下墙牌,蟻群覓食往往會形成一條長長的隊(duì)伍。

??如圖1所示暗甥,在兩點(diǎn)AB之間有兩條路徑喜滨。蟻群準(zhǔn)備在AB之間來回搬運(yùn)食物,由于剛開始路徑1和路徑2上都沒有信息素撤防,因此蟻群隨機(jī)選擇路徑虽风,兩條路徑各占50%。然后由于路徑1比路徑2短寄月,因此單位時(shí)間內(nèi)辜膝,經(jīng)過路徑1的螞蟻更多(假如R_2 = 2R_1,那么路徑1上的螞蟻一個(gè)來回后漾肮,路徑2上的螞蟻才剛到B)厂抖,因此路徑1上的信息素也相對更濃。在這種正反饋機(jī)制的作用下克懊,最終大部分螞蟻都會選擇走更短的路徑1忱辅。

圖1. 信息素作用機(jī)制

3.蟻群算法簡析

原理

受蟻群覓食等行為的啟發(fā),有學(xué)者將其引入到算法中用于解決類似問題谭溉,并提出了蟻群算法墙懂。下面,我們針對一個(gè)具體的有N個(gè)城市的TSP問題夜只,構(gòu)建一個(gè)基礎(chǔ)的ACS蟻群算法來求解垒在。

  1. 首先設(shè)定一個(gè)啟發(fā)值\eta并給他初始化一個(gè)常數(shù),這里我們另 \eta= \frac{1}{L_{ij}}扔亥,其中L_{ij}為城市i與j之間的距離。然后再設(shè)置一個(gè)參數(shù)\tau_{ij}谈为,該參數(shù)表示城市i和j之間的路徑信息素濃度旅挤,算法一開始將其初始化為一個(gè)較小常數(shù)。

  2. 對于N個(gè)城市的TSP問題伞鲫,蟻群算法的基本框架就是將M(M>=N粘茄,一般可設(shè)為1.5N)只螞蟻隨機(jī)分配到N個(gè)城市上,然后總共將算法迭代C次,求得最終所需的最優(yōu)路徑柒瓣。對于第k只螞蟻儒搭,它下一個(gè)選擇去往哪個(gè)城市由如下公式?jīng)Q定:
    p^{k}_{ij} = \begin{cases} \frac{\tau_{ij}^\alpha * \eta_{ij}^\beta}{\sum_{z = J_k(i)}\tau_{iz}^\alpha * \eta_{iz}^\beta},\quad if \ \ j\in J_k(i)\\\\0, \quad \quad otherwise\end{cases}
    這里,p^{k}_{ij}表示第k只螞蟻A_k從城市i運(yùn)動(dòng)到到城市j的概率芙贫,J_k(i)表示第k只螞蟻未經(jīng)過的城市集合(其中i表示螞蟻A_k當(dāng)前所處的城市)搂鲫。\alpha 和\beta分別代表信息濃度\tau與啟發(fā)因子\eta的權(quán)重,這里我們設(shè)置為\alpha=1, \beta=5磺平。

  3. 信息素的更新魂仍。蟻群算法的核心之一在于信息素的更新策略,有部分改進(jìn)的蟻群算法就是針對其信息素更新策略做了優(yōu)化拣挪。這里擦酌,當(dāng)M只螞蟻完成一次路徑遍歷即算法迭代一次后,我們用如下公式進(jìn)行信息素更新:
    \tau_{ij} = (1-\rho)*\tau_{ij} + \Delta\tau_{ij} , \quad \forall (i,j) \in E該公式表示信息素的揮發(fā)機(jī)制菠劝,其中E為N個(gè)城市的鏈接集合赊舶,\rho表示信息素的揮發(fā)率。其中\Delta\tau_{ij}的定義為:\Delta\tau_{ij} = \sum_{k=1}^{M}\Delta\tau_{ij} ^k即每只螞蟻在城市i和j之間殘留的信息素之和赶诊。

ACS - Python實(shí)現(xiàn)
  1. 首先實(shí)現(xiàn)一個(gè)函數(shù)用于生成TSP問題所需的城市集合笼平。
import numpy as np
import matplotlib.pyplot as plt

def creat_city(num, scale):
    """
    input:
        num: 城市數(shù)量
        scale: 城市坐標(biāo)范圍x,y in (0, scale)
    return:
        V:城市的坐標(biāo)集合
        E:城市的鄰接矩陣
    """
    x = np.random.choice(scale, num)
    y = np.random.choice(scale, num)
    
    V = np.stack((x,y), axis=1)
    inner = -2 *  V.dot(V.T)
    xx = np.sum(V**2, axis=1, keepdims=True)
    E = xx + inner + xx.T
    E = E**0.5
    index = [i for i in range(num)]
    #為了防止螞蟻出現(xiàn)自旋,鄰接矩陣上的對角線取值盡量大一點(diǎn)甫何。
    E[index,index] = 9999999
    return V,E

我們使用下面的代碼在400*400的范圍內(nèi)生成20個(gè)城市出吹,其生成的的效果如圖2所示:

V, E = creat_city(20, 400)
plt.scatter(V[:,0], V[:,1], alpha=0.6, c = "r")  # 繪制散點(diǎn)圖,透明度為0.6(這樣顏色淺一點(diǎn)辙喂,比較好看)
plt.show()
圖2. 城市分布圖
  1. 算法所需的其他必備函數(shù)
  • 依概率采樣函數(shù)
import heapq
import random

def a_res(samples, m):
    """
    :samples: [(item, weight), ...]
    :k: number of selected items
    :returns: [(item, weight), ...]
    """

    heap = [] # [(new_weight, item), ...]
    for sample in samples:
        wi = sample[1]
        if wi==0:
            continue
        ui = random.uniform(0, 1)
        ki = ui ** (1/wi)

        if len(heap) < m:
            heapq.heappush(heap, (ki, sample))
        elif ki > heap[0][0]:
            heapq.heappush(heap, (ki, sample))

            if len(heap) > m:
                heapq.heappop(heap)

    return [item[1] for item in heap]
  • 由信息素濃度獲得螞蟻去往下一個(gè)城市的概率捶牢。
def possibility(eta, gamma, other_city, cur_city):
    """
    返回候選城市集合中,從start到各候選城市的概率巍耗,只返回有路徑的
    """   
    alpha = 1
    beta = 5
    start_city = cur_city[-1]

    t_i = gamma[start_city]  
    n_i = eta[start_city]
    
    temp = (t_i**alpha * n_i**beta)
    temp[cur_city] = 0
    add = temp.sum()
    p_ij = temp/add
    
    return p_ij
  • 其他函數(shù)
def rotate(l, n):
    '''
    旋轉(zhuǎn)列表秋麸。
    '''
    return l[n:] + l[:n]

def get_path_dis(root, E):
    """
    獲取該路徑距離。
    """
    dis = E[root[:-1], root[1:]].sum()
    return dis + E[root[0],root[-1]]
  1. 蟻群算法實(shí)現(xiàn)
def ACS(V, E, M, num):
    """
    Ant system
    V : 點(diǎn)集
    E: 鄰接矩陣炬太,點(diǎn)之間的連接性灸蟆,
    M: 螞蟻數(shù)量
    num:迭代次數(shù)
    """
    #相關(guān)參數(shù)
    global_best_path=None   #當(dāng)前最優(yōu)路徑
    global_best_dis = 99999999
    cur_city = None
    other_city = [i for i in range(len(V))]
    lo = 0.5   #信息素?fù)]發(fā)率
    
    
    #信息素啟發(fā)值
    eta = 1/E
    eta[np.isinf(eta)] = 0
    
    #信息素濃度
    E_mean = E[E>0].mean()
    gamma = np.full(E.shape, 1/len(V))
    
    V_index = [i for i in range(len(V))]

    for i in range(num):
        epoch_gamma = np.zeros_like(gamma) #保存每一輪的各路徑信息素累積量
        local_best_path=None   #每一次迭代當(dāng)前最優(yōu)路徑
        local_best_dis = 99999999
        for j in range(M):
            cur_city = [j%len(V)]
            other_city = [i for i in range(len(V))]
            other_city.remove(cur_city[-1])
            while(other_city):
                p_ij = possibility(eta, gamma, other_city, cur_city)
 
                next_city = int(a_res(np.stack((V_index,p_ij),axis=1), 1)[0][0])
                
                epoch_gamma[cur_city[-1],next_city] += gamma[cur_city[-1],next_city]
                cur_city.append(next_city)
                other_city.remove(next_city)
            epoch_dis = get_path_dis(cur_city, E)
            if epoch_dis < local_best_dis:
                local_best_dis = epoch_dis
                local_best_path = cur_city

        if local_best_dis < global_best_dis:
            global_best_dis = local_best_dis
            global_best_path = local_best_path
            
        gamma = (1 - lo) * gamma + epoch_gamma
    
    print("The shortest distance is {}m and the best path is: ".format(global_best_dis), end="")
    best_path = rotate(global_best_path, global_best_path.index(0))
    for index in best_path:
        print(" city_" + str(index) + " ->", end="")
    print("city_0\n")
    
    return best_path
  1. 算法計(jì)算結(jié)果
root = ACS(V, E, 20, 50)
path = V[root]
path = np.append(path, [path[0]], axis=0)
plt.plot(path[:,0], path[:,1], marker="o", mfc="r")
plt.show()
圖3. ACS計(jì)算結(jié)果
MMAS(最大-最小蟻群系統(tǒng))
  1. MMAS算法簡介
    我們從圖3中可以看到,ACS算法對于20個(gè)城市的TSP問題已經(jīng)能夠得到相對較優(yōu)的結(jié)果了亲族。但是ACS仍然有一些缺點(diǎn)炒考,比如收斂速度比較慢,獲取容易陷入局部最小值等等霎迫。針對這些缺陷有學(xué)者又提出了最大-最小蟻群系統(tǒng)斋枢。該改進(jìn)算法與基本的蟻群算法有以下幾點(diǎn)不同
  • \tau的值被限制在一定范圍內(nèi)[\tau_{min}, \tau_{max}],這也正是該算法名字的由來知给。通常該最大 最小值有我們自己設(shè)定瓤帚。
  • 算法開始將城市之間的信息素濃度都初始化為\tau_{max}描姚。
  • 每次迭代信息素更新時(shí)只針對最優(yōu)路徑進(jìn)行更新,而不考慮其他路徑戈次。其中最優(yōu)路徑可為局部最優(yōu)路徑(本次迭代最優(yōu))或全局最優(yōu)路徑(所有迭代最優(yōu))轩勘。具體更新公式如下:
    \tau_{ij} = (1 - \rho)*\tau_{ij} , \quad \forall (i,j) \in E \tau_{ij} = \tau_{ij} + \Delta\tau_{ij}^{best},\quad \forall (i,j) \in Route^{best}其中怯邪,Route^{best}指本次迭代最優(yōu)路徑绊寻,\Delta\tau_{ij}^{best}為本次迭代最優(yōu)路徑中從城市i到城市j的信息素增量。增量的定義為\Delta\tau_{ij}^{best} = \frac{Q}{L^{best}},其中Q為一個(gè)自己設(shè)置的常數(shù)擎颖,L^{best}是本次迭代最優(yōu)路徑的長度榛斯。
  1. MMAS算法python實(shí)現(xiàn)
def MMAS(V, E, M, num, islocal=True):
    """
    最大最小蟻群算法
    V : 點(diǎn)集
    E: 鄰接矩陣,點(diǎn)之間的連接性搂捧,
    M: 螞蟻數(shù)量
    num:迭代次數(shù)
    """
    #相關(guān)參數(shù)
    global_best_path=None   #當(dāng)前最優(yōu)路徑
    global_best_dis = 99999999
    cur_city = None
    other_city = [i for i in range(len(V))]
    lo = 0.8   #信息素?fù)]發(fā)率
    e = num #精英路徑權(quán)重
    
    tao_min = 0.1 / num
    tao_max = 1

    #信息素啟發(fā)值
    eta = 1/E
    eta[np.isinf(eta)] = 0
    
    #信息素濃度
    E_mean = E[E>0].mean()
    gamma = np.full(E.shape,tao_max) 
    
    V_index = [i for i in range(len(V))]

    for i in range(num):
        epoch_gamma = np.zeros_like(gamma) #保存每一輪的各路徑信息素累積量
        local_best_path=None   #每一次迭代當(dāng)前最優(yōu)路徑
        local_best_dis = 99999999
        for j in range(M):
            cur_city = [j%len(V)]
            other_city = [i for i in range(len(V))]
            other_city.remove(cur_city[-1])
            while(other_city):
                p_ij = possibility(eta, gamma, other_city, cur_city)
                next_city = int(a_res(np.stack((V_index,p_ij),axis=1), 1)[0][0])
                if next_city not in other_city:
                    next_city = int(a_res(np.stack((V_index,p_ij),axis=1), 1)[0][0])
                
                epoch_gamma[cur_city[-1],next_city] += gamma[cur_city[-1],next_city]
                cur_city.append(next_city)
                other_city.remove(next_city)
            epoch_dis = get_path_dis(cur_city, E)
            if epoch_dis < local_best_dis:
                local_best_dis = epoch_dis
                local_best_path = cur_city

        if local_best_dis < global_best_dis:
            global_best_dis = local_best_dis
            global_best_path = local_best_path
         #信息素更新   
        gamma = (1 - lo) * gamma
        if islocal:
            for i,j in np.stack((local_best_path[1:] + local_best_path[:1], local_best_path), axis=1):
                gamma[i,j] += e / local_best_dis
        else:
            for i,j in np.stack((global_best_path[1:] + global_best_path[:1], global_best_path), axis=1):
                gamma[i,j] += e / global_best_dis
        gamma[gamma>tao_max] = tao_max
        gamma[gamma<tao_min] = tao_min
    
    print("The shortest distance is {}m and the best path is: ".format(global_best_dis), end="")
    best_path = rotate(global_best_path, global_best_path.index(0))
    for index in best_path:
        print(" city_" + str(index) + " ->", end="")
    print("city_0.\n")
    
    return best_path
  1. MMAS算法運(yùn)行結(jié)果
    從圖4可以看到驮俗,MMAS對于TSP問題能得到很好的解,并且收斂速度也比較快允跑。因此一般對于TSP類問題使用MMAS來求解是比較合適的王凑。


    圖4. MMAS結(jié)果
蟻群算法的其他優(yōu)化版本

這里我們限于篇幅僅僅簡單介紹了ACS和MMAS,其實(shí)對于蟻群算法還有精英系統(tǒng)(EAS)聋丝、排序蟻群(ASrank)索烹、連續(xù)正交蟻群(COAC)等等。此外弱睦,我們這里僅僅是用TSP算法來舉例百姓,實(shí)際上,蟻群算法可以被應(yīng)用到許多問題上况木,比如調(diào)度問題垒拢、設(shè)置問題、分配問題火惊、車輛路徑問題等等求类。讀者朋友如果有興趣可以去擴(kuò)展這些知識,相信會很有收獲的屹耐!

4.參考資料

https://zh.wikipedia.org/wiki/%E8%9A%81%E7%BE%A4%E7%AE%97%E6%B3%95
https://www.voidking.com/dev-aco/
https://zhuanlan.zhihu.com/p/45985636
https://blog.csdn.net/zuochao_2013/article/details/71872950
https://blog.csdn.net/acsunqi/article/details/76060669
https://cloud.tencent.com/developer/article/1369213

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末尸疆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子惶岭,更是在濱河造成了極大的恐慌寿弱,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件按灶,死亡現(xiàn)場離奇詭異脖捻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)兆衅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人羡亩,你說我怎么就攤上這事摩疑。” “怎么了畏铆?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵雷袋,是天一觀的道長。 經(jīng)常有香客問我辞居,道長楷怒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任瓦灶,我火速辦了婚禮鸠删,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贼陶。我一直安慰自己刃泡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布碉怔。 她就那樣靜靜地躺著烘贴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撮胧。 梳的紋絲不亂的頭發(fā)上桨踪,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機(jī)與錄音芹啥,去河邊找鬼锻离。 笑死,一個(gè)胖子當(dāng)著我的面吹牛叁征,可吹牛的內(nèi)容都是我干的纳账。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼捺疼,長吁一口氣:“原來是場噩夢啊……” “哼疏虫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起啤呼,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤卧秘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后官扣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體翅敌,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年惕蹄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蚯涮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片治专。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖遭顶,靈堂內(nèi)的尸體忽然破棺而出张峰,到底是詐尸還是另有隱情,我是刑警寧澤棒旗,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布喘批,位于F島的核電站,受9級特大地震影響铣揉,放射性物質(zhì)發(fā)生泄漏饶深。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一逛拱、第九天 我趴在偏房一處隱蔽的房頂上張望敌厘。 院中可真熱鬧,春花似錦橘券、人聲如沸额湘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锋华。三九已至,卻和暖如春箭窜,著一層夾襖步出監(jiān)牢的瞬間毯焕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工磺樱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纳猫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓竹捉,卻偏偏與公主長得像芜辕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子块差,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容