1.建立模型
1.1 問題描述
有一個原材料供應商,從工廠出發,用一輛貨車爲若干個下游工廠配送原材料且只配送一次,最後仍回到工廠,問應如何選擇行車路線,使總耗油量最少。 模型的基本假設:
a.貨車的載重量無限大。
b.貨車行駛路面的粗糙程度相同並且無上下坡。
c.貨車的耗油量只與貨車的載重量和行駛路程決定,其他因數忽略不計。
耗油量的多少可以用貨車在整個過程中做功的多少來衡量,
(PS:將這個問題完整描述將有助於整個算法的理解)
1.3模型目標
在本模型中,在每一行或每一列中各取一個值得到貨車的一條行車路徑,共有n!種貨車行駛路線(n爲下游工廠數量),目標是通過有限次計算,尋找一條行車路徑,使總的耗油量相對最低。1.4模型分析
本問題的組合情況數量爲n!,其中還未考慮其中每一種情況本身的計算量。
當n=10,n!=3628800;
當n=20,n!=2.432902e+18;
當n=50,n!=3.041409e+64;
當n=100,n!=9.332622e+157;
......
當n=100時,一般個人計算機的頻率約爲2GHz,這樣需要持續計算n!/(2*e+9)=4.666311e+148秒~1.479677e+141年,列舉所有情況將產生組合爆炸,要窮舉所有的情況基本是不可能的,這也是一類典型的np-hard問題。當n很大時,按照常規的方法根本沒法解決此類問題,一般用啓發式算法來解決此類問題,常用的啓發式算法有:遺傳算法、模擬退火算法、禁忌搜索算法、蟻羣算法等,但啓發式算法也只能獲得相對最優解,我將用蟻羣算法(AntColony Optimization)來解決本問題。
2.蟻羣算法(AntColony Optimization)
2.1 基本思想
蟻羣算法是受到人們對自然界中真實蟻羣的集體行爲的研究成果的啓發而提出的一種基於種羣的模擬進化算法,屬於隨機搜索算法,由意大利學者M.Dorigo等人首先提出。螞蟻在行動過程中會在路徑上留下信息素,這些物質信息能被同一蟻羣中後來的螞蟻感知到並作爲一種信號影響後到的螞蟻的路徑的選擇,後到的螞蟻選擇這些路徑的可能性比沒有這些物質的路徑的可能性大的多,並且路徑上的信息素濃度愈大,被後面的螞蟻選擇的可能性就愈大。
蟻羣算法信息素的更新方式有局部更新和全局更新,在本問題中,局部更新在構建一條合法路徑的過程中進行信息素的更新,當螞蟻走過一條邊之後,就對該邊進行信息素的更新;全局更新是在所有螞蟻都構建了一條合法路徑之後纔對各邊進行信息素更新。蟻羣算法在解決具體問題時總共有三種模型:蟻量模型、蟻密模型和蟻周模型,蟻量模型和蟻密模型的信息素更新方式爲局部更新,蟻量模型中,螞蟻在自己所走過的邊上釋放的信息素是邊的長度的倒數,蟻密模型中,螞蟻在自己所走過的邊上釋放的信息素是一個常量,蟻周模型的信息素更新方式是全局更新,但是進行全局更新時要記錄螞蟻所走過的所有路徑,這在下游工廠數量較多的本問題中是很難實現的,本問題所用的蟻羣算法採用蟻量模型。
2.2 算法流程
本問題的蟻羣算算法的流程圖爲:
2.3 C語言實現
我用C語言通過蟻羣算法解決本問題,c語言實現的代碼如下,共有兩個文件,ACO.h:記錄一些全局常量和函數聲明,ACO.c爲主要實現文件。ACO.h和 ACO.c的代碼:
/*
============================================================================
Name : ACO.c
Author : sheng
Description : 此模型採取信息素局部更新方式,爲蟻量模型:螞蟻在自己所走過的邊上釋
放的信息素是螞蟻走過邊的長度的倒數
============================================================================
*/
#include
#include
#include
#include
#include "ACO.h"
int main()
{
int i,j,interationCount=0; /*定義迭代次數指標並初始化爲0*/
int robustness=0; /*用於判斷是否要減小信息素傳遞參數rou和確定性性搜索或探索性搜索q0的值*/
int stopFlag=0; /*算法停止標誌*/
int pheromoneRoute[FACTORY_COUNT+2]={0}; /*定義信息素濃度最大的路徑並初始化爲0*/
double pheromoneBasedTotalWork; /*按信息素選擇的路徑的總功*/
double pheromoneDeliverConst=ROU_MAX; /*信息素傳遞參數,初始化爲最大值*/
double searchChooseConst=SEARCH_CHOOSE_MAX; /*探索性方式選擇參數,初始化爲最大值*/
double distance[FACTORY_COUNT+1][FACTORY_COUNT+1];
double pheromone[FACTORY_COUNT+1][FACTORY_COUNT+1];
struct factory factoryInfo[FACTORY_COUNT+1]; /*定義每個工廠(包括原材料配送中心)的信息*/
struct ant antInfo[ANT_COUNT]; /*定義螞蟻信息的結構體,包括螞蟻以走的路徑信息*/
Init_Factory_Information(factoryInfo); /*初始化各個下游工廠以及供應商的位置及需求信息*/
Init_Distance_Information(factoryInfo,distance); /*初始化各個下游工廠以及供應商的距離矩陣*/
Init_Pheromone_Information(pheromone); /*初始化各個下游工廠以及供應商之間的信息素濃度,無單位,爲具體值*/
Ant_Information(antInfo); /*初始化螞蟻信息*/
while ((stopFlag");
else printf("\n");
}
return EXIT_SUCCESS;
}
/*
============================================================================
Function Name:Update_Row_Q
Function :更新信息素傳遞參數和搜索選擇的值
============================================================================
*/
void Update_Row_Q(double *pheromoneDeliverConst,double *searchChooseConst,int *robustness,int *stopFlag)
{
if (*robustness==ROBUSTNESS){
*robustness=0; /*清零*/
(*pheromoneDeliverConst)*=ROU_CHANGE_RANGE; /*信息素傳遞參數乘以ROU_CHANGE_RANGE ,減小*/
if (*pheromoneDeliverConst<=ROU_MIN) *pheromoneDeliverConst=ROU_MIN; /*ROU不能無限小,確保收斂*/
(*searchChooseConst)*=ROU_CHANGE_RANGE; /*探索方式參數乘以ROU_CHANGE_RANGE,減小*/
if (*searchChooseConst<=SEARCH_CHOOSE_MIN) *searchChooseConst=SEARCH_CHOOSE_MIN; /*探索方式選擇參數q0不能無限小,確保收斂*/
}
if (*pheromoneDeliverConst==ROU_MIN) (*stopFlag)++;
return ;
}
/*
============================================================================
Function Name:Based_Pheromone_Route
Function :找出信息素濃度最大的路徑,即螞蟻最終都會走的路徑
============================================================================
*/
void Based_Pheromone_Route(double (*pheromone)[FACTORY_COUNT+1],int *pheromoneRoute,int *robustness)
{
int i,j,k;
int choosePoint,flag,choosePointFalg,robustnessFlag; /*選擇點、退出標誌、選擇標誌、選擇點標誌、魯棒性檢查標誌*/
int pheromoneRoutefront[FACTORY_COUNT+2]; /*用於裝上一次得到的信息素的路徑信息*/
int tabuNumber; /*禁忌的數量*/
tabuNumber=1;
robustnessFlag=0;
for (i=0;iantInfo[j].totalWork){ /* 按總功大小從小到大排序*/
totalWorkChange=antInfo[i].totalWork; /* 交換總功*/
antInfo[i].totalWork=antInfo[j].totalWork;
antInfo[j].totalWork=totalWorkChange;
for (k=0;k
數據來源說明:本例子中的FACTORY_COUNT個工廠的位置、以及各個工廠的需求數據(共(FACTORY_COUNT*3)個數)均是通過OpenOffice Calc的隨機數生成的。
FACTORY的X與Y座標生成算式均爲:IF(RAND()>0.5;ROUND((RAND()*(1000-100)+100);2);-ROUND((RAND()*(1000-100)+100);2))。X與Y座標代有正負,生成的正負值的概率基本相等,式子中的三個RAND()函數在每一次都是獨立取值的,說明了本數據服從(-1000,100)U(100,1000)之間的均勻分佈,配送中心的座標爲(0,0)。
FACTORY的需求生成算式:ROUND((RAND()*(100-10)+10);2)。各個下游工廠的需求量服從(10,100)之間的均勻分佈,保留兩個有效數字,配送中心的需求爲0。本例中數據沒能直觀的體現數據本身的意義,只是將其用到本問題算法的驗證中。代碼參數說明:本例中存在許多全局參數設置,如ALPHA,BETA,CONSTRICTION_CONST,INTERATION_COUNT,SEARCH_CHOOSE_MAX,SEARCH_CHOOSE_MIN,ROU_MAX,ROU_MIN,ROU_CHANGE_RANGE,SEARCH_CHOOSE_RANGE,INIT_PHEROMONE等,比較重要的參數是ALPHA和BETA,SEARCH_CHOOSE_RANGE,ALPHA表示信息素在概率計算中的權重,值越大,信息素在螞蟻選擇下一個所要到達的點的作用越大,BETA表示啓發因子在概率計算中的權重,值越大,啓發因子在螞蟻選擇下一個所要到達的點的作用越大,即螞蟻有更大的可能會去走新的路徑;SEARCH_CHOOSE_RANGE表示信息素的衰減程度,即每進行一次迭代,就會進行一次信息素的衰減,然後逐次遞減,信息素對於螞蟻選擇的影響作用也越小。
C語言實現的輸出結果:
由於使用啓發式算法解決np-hard問題,幾乎不可能得到最優解,在本例中,隨着參數(全局常量)設置的不同,結果不相同,即使在參數設置相同的情況下,運行的結果也很難相同,因爲在信息素的作用減弱之後,螞蟻可以隨機選擇路線,而這種隨機性也就導致不同的結果。從結果可看出,第一組結果表示的是從最後一次迭代中選擇標號爲0~9的螞蟻,輸出他們在最後一次行走的路線的總功(總功與耗油量成正比,總功越小,耗油量越少,結果越優),從中可以看出,不同螞蟻走的路線不盡相同;第二組結果表示通過蟻羣算法得出的相對最優解,明顯比上面隨機選擇一組的結果要好,後面的一組線路圖表示相對最優解所對應的行車路線,該線路表示從配送中心(0,0)出發,經過FACTORY_COUNT個(本例中是100個)下游工廠後回到配送中心(0,0)。
總結:本例通過蟻羣算法解決原材料配送這個np-hard問題,通過C語言實現了基本的算法並求出了一組相對最優解,但是本文對算法中參數選擇、數據選擇以及最終結果的解釋沒能做有效地分析,這需要後續不斷地修正和改進。
PS:本算例是我在大二時候實現的(遺傳算法實現會在後續整理出來),始終感覺這個簡單的模型對於算法的學習非常有用,這幾天就把這個模型以及其蟻羣算法實現整理出來,希望這個簡單的模型能夠能夠引導讀者對算法的思考,加深對蟻羣算法的理解。
參考:
[1] 童若鋒,張維澤,許星,董金祥. 基於改進蟻羣算法的物流配送路徑優化.浙江大學人工智能研究所.
[2] 張居陽,孫吉貴. 組合優化調度問題求解方法. 計算機科學,2003,30(2).
[3] 詹士昌 徐婕 吳俊. 蟻羣算法中有關算法參數的最優選擇. 科技通報,第19卷第5期, 2003 年9 月