原材料配送問題的遺傳算法實現(C語言)

原材料配送問題的遺傳算法實現


1.建立模型
      1.1 問題描述

           有一個原材料供應商,從工廠出發,用一輛貨車爲若干個下游工廠配送原材料且只配送一次,最後仍回到工廠,問應如何選擇行車路線,使總耗油量最少。模型的基本假設:

                a.貨車的載重量無限大。
                b.貨車行駛路面的粗糙程度相同並且無上下坡。
                c.貨車的耗油量只與貨車的載重量和行駛路程決定,其他因數忽略不計。

      1.2 模型推導(通過截圖體現)

          耗油量的多少可以用貨車在整個過程中做功的多少來衡量,

  

     

  

  

  

  

        (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很大時,按照常規的方法根本沒法解決此類問 題,一般用啓發式算法來解決此類問題,常用的啓發式算法有:遺傳算法、模擬退火算法、禁忌搜索算法、蟻羣算法等,但啓發式算法也只能獲得相對最優解,繼上次用蟻羣算法實現這個模型之後(參見:原材料配送問題的蟻羣算法實現),我將用遺傳算法(Genetic Algorithm )來解決本問題。

2.遺傳算法(Genetic Algorithm )
     2.1 基本思想
        遺傳算法(Genetic Algorithms,簡稱GA)是J.HolIand於1975年受生物進化論的啓發而提出的。遺傳算法是建立在自然選擇和遺傳理論基礎上,將生物進化過程中適者生存規則與同一羣染色體的隨機信息變換機制相結合的一種高度並行、隨機和自適應的優化算法。

     2.2算法核心環節

      本節將介紹遺傳算法實現過程中的核心環節,包括:編碼、交叉、變異、交叉變異個體的概率分配,這部分涉及到公式和特殊字符,就直接上MS Word上編輯好,直接通過截圖體現。

     2.2.1編碼Encoding

  

     2.2.2 交叉 Crossover

  

  

     2.2.3 變異 Mutation


     2.2.4 交叉變異個體的概率分配

  

  

  
   2.3 算法流程
    本問題的蟻羣算算法的流程圖爲:

              

     2.4 C語言實現
       我用C語言通過遺傳算法解決本問題,c語言實現的代碼如下,共有兩個文件,GA.H:記錄一些全局常量和函數聲明,GA.c爲主要實現文件。
      GA.H和  ACO.c的代碼:  

/*
 ============================================================================
 Name        : GA.c
 Author      : sheng
 Description : 本例通過遺傳算法解決原材料配送這個np-hard問題,求出了一組相對
               最優解以及一組參照解。
 ============================================================================
 */
#include 
#include 
#include 
#include 
#include "GA.H"
int main(){
	int j,k;
	int fittnessOrder;
	struct factory  factoryInfo[FACTORY_COUNT+1];    //定義每個工廠(包括原材料配送中心)的信息
	struct route  population[INIT_POPULATION_NUM];   //定義種羣每個個體的路徑
	fittnessOrder=10;   //結果輸出按適應度大小降序排列的前fittnessOrder位
	Init_Factory_Information(factoryInfo);
	Init_Population(population,factoryInfo);
        Evaluate_Population(population,factoryInfo);
	for (k=0;k");
	  	else printf("\n");
	}
	printf("\n");
	printf("2.The relative optimal individual: \n");
	printf("The fittness: %f\n",((1/population[0].fittness)*1000*FRICTION_RATIO));//輸出相對最優種羣個體的適應度
	printf("The route: ");
	for(j=0;j");
	        else printf("\n");
	}
	return EXIT_SUCCESS;
}
/*
============================================================================
Function Name:Crossover
Function     :種羣個體交叉操作,產生新的子代
============================================================================
*/
void Crossover(struct route *population){
	int i;
	int crossvoerIndividualA,crossvoerIndividualB;  //交叉的兩個個體
	int crossoverPosition[2]={0};            //交叉變異的起始地址和長度
	for (i=0;i9);
	return ;
}
/*
============================================================================
Function Name:Evaluate_Population
Function     :評估種羣,產生適應度和按適應度排序的結果
============================================================================
*/
void Evaluate_Population(struct route *population,struct factory *factoryInfo){
	int i,j,k,routeChange;             //routeChange交換路徑信息的中間變量
	double fittnessChange;              //交換適應度的中間變量
	for (j=0;j

      數據來源說明:本例子中的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。
    本例中數據沒能直觀的體現數據本身的意義,只是將其用到本問題算法的驗證中。代碼參數說明:本例中存在許多全局參數設置,如 INIT_POPULATION_NUM,GENERATION_NUM,POPULATION_PARENT_NUM,POPULATION_CHILDREN_NUM,MUTATION_NUM等,比較重要的參數爲,INIT_POPULATION_NUM表示種羣個體總數數,這個參數直接決定了問題的規模,種羣個體總數需要根據所解決的問題和計算機的硬件配置選擇,這個參數設定的太大會造成內存不足,但設定的太小,會造成難於收斂,不能求出可接受的相對最優解; GENERATION_NUM表示迭代次數,從一定程度上來說,迭代的次數越多越好,但一旦出現早熟後,此時迭代只是浪費系統資源,不會對結果產生影響; POPULATION_PARENT_NUM表示每一代沒有進行交叉變異的個體數量,與 POPULATION_CHILDREN_NUM相對,POPULATION_CHILDREN_NUM表示每一代進行了交叉變異的個體數量, POPULATION_PARENT_NUM設定的越大,越容易收斂甚至是早熟;MUTATION_NUM表示同一條染色體上變異次數的取值範圍爲(0,MUTATION_NUM),通過隨機數決定這個參數的具體值。

  C語言實現的輸出結果:

  
       由於使用啓發式算法解決np-hard問題,幾乎不可能得到最優解,在本例中,隨着參數(全局常量)設置的不同,結果不相同,即使在參數設置相同的情況下,運行的結果也很難相同,但遺傳算法一旦早熟之後,每個個體的染色體都是相同的,最終出現每個種羣個體的路徑都相同的情況。 第一組結果表示其中的第POPULATION_PARENT_NUM個個體(種羣中第一個從上一代變異而來的個體)的適應度和染色體編號(行車路線)。第二組結果表示通過遺傳算法得出的相對最優解,比上面隨機選擇一組的結果要好,後面的一組線路圖表示相對最優解所對應的行車路線(染色體編號),該線路表示從配送中心(0,0)出發,經過FACTORY_COUNT個(本例中是100個)下游工廠後回到配送中心(0,0)。


總結:本例通過遺傳算法解決原材料配送這個np-hard問題,通過C語言實現了基本的遺傳算法並求出了一組相對最優解,本算例的參數選擇較蟻羣算法簡單,實際操作的時候很容易造成內存不足問題,代碼上體現地很詳細。但是本文對算法中參數選擇、數據選擇以及最終結果的解釋沒能做有效地分析,只是做了部分嘗試,這需要後續不斷地修正和改進。

PS:本算例是我在大二時候實現的,本文模板是參照前幾天寫的博客:原材料配送問題的蟻羣算法實現(C語言) ,這兩個實現方式針對同一問題,模板就直接Copy了,希望這個簡單的模型及其遺傳和蟻羣算法實現,能夠加深讀者對於蟻羣算法、遺傳算法等啓發式算法的理解。


參考:

[1] 柳林,朱建榮. 基於遺傳算法的物流配送路徑優化問題的研究. 計算機工程與應用,2005 年第41 卷第27 期:227-229.
[2] 郎茂祥,胡思繼. 用混合遺傳算法求解物流配送路徑優化問題的研究. 中國管理科學,2002,10(10):51-56.
[3] 魯燃,馬學強,潘美芹. 利用遺傳算法搜索全局最優的一種混合算法. 山東師範大學報(自然科學版),2000,15(4)


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