優(yōu)化算法入門系列文章目錄(更新中): 1. 模擬退火算法 2. 遺傳算法
遺傳算法 ( GA , Genetic Algorithm ) ,也稱進化算法 。 遺傳算法是受達爾文的進化論的啟發(fā),借鑒生物進化過程而提出的一種啟發(fā)式搜索算法。因此在介紹遺傳算法前有必要簡單的介紹生物進化知識。
一.進化論知識作為遺傳算法生物背景的介紹,下面內(nèi)容了解即可: 種群(Population):生物的進化以群體的形式進行,這樣的一個群體稱為種群。 個體:組成種群的單個生物。 基因 ( Gene ) :一個遺傳因子。 染色體 ( Chromosome ) :包含一組的基因。 生存競爭,適者生存:對環(huán)境適應度高的、牛B的個體參與繁殖的機會比較多,后代就會越來越多。適應度低的個體參與繁殖的機會比較少,后代就會越來越少。 遺傳與變異:新個體會遺傳父母雙方各一部分的基因,同時有一定的概率發(fā)生基因變異。
簡單說來就是:繁殖過程,會發(fā)生基因交叉( Crossover ) ,基因突變 ( Mutation ) ,適應度( Fitness )低的個體會被逐步淘汰,而適應度高的個體會越來越多。那么經(jīng)過N代的自然選擇后,保存下來的個體都是適應度很高的,其中很可能包含史上產(chǎn)生的適應度最高的那個個體。
二.遺傳算法思想借鑒生物進化論,遺傳算法將要解決的問題模擬成一個生物進化的過程,通過復制、交叉、突變等操作產(chǎn)生下一代的解,并逐步淘汰掉適應度函數(shù)值低的解,增加適應度函數(shù)值高的解。這樣進化N代后就很有可能會進化出適應度函數(shù)值很高的個體。 舉個例子,使用遺傳算法解決“0-1背包問題”的思路:0-1背包的解可以編碼為一串0-1字符串(0:不取,1:?。?;首先,隨機產(chǎn)生M個0-1字符串,然后評價這些0-1字符串作為0-1背包問題的解的優(yōu)劣;然后,隨機選擇一些字符串通過交叉、突變等操作產(chǎn)生下一代的M個字符串,而且較優(yōu)的解被選中的概率要比較高。這樣經(jīng)過G代的進化后就可能會產(chǎn)生出0-1背包問題的一個“近似最優(yōu)解”。
編碼:需要將問題的解編碼成字符串的形式才能使用遺傳算法。最簡單的一種編碼方式是二進制編碼,即將問題的解編碼成二進制位數(shù)組的形式。例如,問題的解是整數(shù),那么可以將其編碼成二進制位數(shù)組的形式。將0-1字符串作為0-1背包問題的解就屬于二進制編碼。
遺傳算法有3個最基本的操作:選擇,交叉,變異。
選擇:選擇一些染色體來產(chǎn)生下一代。一種常用的選擇策略是 “比例選擇”,也就是個體被選中的概率與其適應度函數(shù)值成正比。假設(shè)群體的個體總數(shù)是M,那么那么一個體Xi被選中的概率為f(Xi)/( f(X1) + f(X2) + …….. + f(Xn) ) 。比例選擇實現(xiàn)算法就是所謂的“輪盤賭算法”( Roulette Wheel Selection ) ,輪盤賭算法的一個簡單的實現(xiàn)如下:
輪盤賭算法
/*
* 按設(shè)定的概率,隨機選中一個個體 * P[i]表示第i個個體被選中的概率 */ int RWS() { m =0; r =Random(0,1); //r為0至1的隨機數(shù) for(i=1;i<=N; i++) { /* 產(chǎn)生的隨機數(shù)在m~m+P[i]間則認為選中了i * 因此i被選中的概率是P[i] */ m = m + P[i]; if(r<=m) return i; } }
交叉(Crossover):2條染色體交換部分基因,來構(gòu)造下一代的2條新的染色體。例如: 交叉前: 00000|011100000000|10000 11100|000001111110|00101 交叉后: 00000|000001111110|10000 11100|011100000000|00101 染色體交叉是以一定的概率發(fā)生的,這個概率記為Pc 。
變異(Mutation):在繁殖過程,新產(chǎn)生的染色體中的基因會以一定的概率出錯,稱為變異。變異發(fā)生的概率記為Pm 。例如: 變異前: 000001110000000010000 變異后: 000001110000100010000
適應度函數(shù) ( Fitness Function ):用于評價某個染色體的適應度,用f(x)表示。有時需要區(qū)分染色體的適應度函數(shù)與問題的目標函數(shù)。例如:0-1背包問題的目標函數(shù)是所取得物品價值,但將物品價值作為染色體的適應度函數(shù)可能并不一定適合。適應度函數(shù)與目標函數(shù)是正相關(guān)的,可對目標函數(shù)作一些變形來得到適應度函數(shù)。
三.基本遺傳算法的偽代碼
基本遺傳算法偽代碼
/*
* Pc:交叉發(fā)生的概率 * Pm:變異發(fā)生的概率 * M:種群規(guī)模 * G:終止進化的代數(shù) * Tf:進化產(chǎn)生的任何一個個體的適應度函數(shù)超過Tf,則可以終止進化過程 */ 初始化Pm,Pc,M,G,Tf等參數(shù)。隨機產(chǎn)生第一代種群Pop do { 計算種群Pop中每一個體的適應度F(i)。 初始化空種群newPop do { 根據(jù)適應度以比例選擇算法從種群Pop中選出2個個體 if ( random ( 0 , 1 ) < Pc ) { 對2個個體按交叉概率Pc執(zhí)行交叉操作 } if ( random ( 0 , 1 ) < Pm ) { 對2個個體按變異概率Pm執(zhí)行變異操作 } 將2個新個體加入種群newPop中 } until ( M個子代被創(chuàng)建 ) 用newPop取代Pop }until ( 任何染色體得分超過Tf, 或繁殖代數(shù)超過G )
四.基本遺傳算法優(yōu)化下面的方法可優(yōu)化遺傳算法的性能。 精英主義(Elitist Strategy)選擇:是基本遺傳算法的一種優(yōu)化。為了防止進化過程中產(chǎn)生的最優(yōu)解被交叉和變異所破壞,可以將每一代中的最優(yōu)解原封不動的復制到下一代中。 插入操作:可在3個基本操作的基礎(chǔ)上增加一個插入操作。插入操作將染色體中的某個隨機的片段移位到另一個隨機的位置。
五. 使用AForge.Genetic解決TSP問題AForge.NET是一個C#實現(xiàn)的面向人工智能、計算機視覺等領(lǐng)域的開源架構(gòu)。AForge.NET中包含有一個遺傳算法的類庫。
AForge.NET主頁:http://www./ AForge.NET代碼下載:http://code.google.com/p/aforge/
介紹一下AForge的遺傳算法用法吧。AForge.Genetic的類結(jié)構(gòu)如下: 圖1. AForge.Genetic的類圖
下面用AForge.Genetic寫個解決TSP問題的最簡單實例。測試數(shù)據(jù)集采用網(wǎng)上流傳的中國31個省會城市的坐標:
13042312
36391315 41772244 37121399 34881535 33261556 32381229 41961004 4312790 4386570 30071970 25621756 27881491 23811676 1332695 37151678 39182179 40612370 37802212 36762578 40292838 42632931 34291908 35072367 33942643 34393201 29353240 31403550 25452357 27782826 23702975
操作過程: (1) 下載AForge.NET類庫,網(wǎng)址:http://code.google.com/p/aforge/downloads/list (2) 創(chuàng)建C#空項目GenticTSP。然后在AForge目錄下找到AForge.dll和AForge.Genetic.dll,將其拷貝到TestTSP項目的bin/Debug目錄下。再通過“Add Reference...”將這兩個DLL添加到工程。 (3) 將31個城市坐標數(shù)據(jù)保存為bin/Debug/Data.txt 。 (4) 添加TSPFitnessFunction.cs,加入如下代碼:
TSPFitnessFunction類
using System;
using AForge.Genetic; namespace GenticTSP { ///<summary> /// Fitness function for TSP task (Travaling Salasman Problem) ///</summary> publicclass TSPFitnessFunction : IFitnessFunction { // map privateint[,] map =null; // Constructor public TSPFitnessFunction(int[,] map) { this.map = map; } ///<summary> /// Evaluate chromosome - calculates its fitness value ///</summary> publicdouble Evaluate(IChromosome chromosome) { return1/ (PathLength(chromosome) +1); } ///<summary> /// Translate genotype to phenotype ///</summary> publicobject Translate(IChromosome chromosome) { return chromosome.ToString(); } ///<summary> /// Calculate path length represented by the specified chromosome ///</summary> publicdouble PathLength(IChromosome chromosome) { // salesman path ushort[] path = ((PermutationChromosome)chromosome).Value; // check path size if (path.Length != map.GetLength(0)) { thrownew ArgumentException("Invalid path specified - not all cities are visited"); } // path length int prev = path[0]; int curr = path[path.Length -1]; // calculate distance between the last and the first city double dx = map[curr, 0] - map[prev, 0]; double dy = map[curr, 1] - map[prev, 1]; double pathLength = Math.Sqrt(dx * dx + dy * dy); // calculate the path length from the first city to the last for (int i =1, n = path.Length; i < n; i++) { // get current city curr = path[i]; // calculate distance dx = map[curr, 0] - map[prev, 0]; dy = map[curr, 1] - map[prev, 1]; pathLength += Math.Sqrt(dx * dx + dy * dy); // put current city as previous prev = curr; } return pathLength; } } }
(5) 添加GenticTSP.cs,加入如下代碼:
GenticTSP類
using System;
using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using AForge; using AForge.Genetic; namespace GenticTSP { class GenticTSP { staticvoid Main() { StreamReader reader =new StreamReader("Data.txt"); int citiesCount =31; //城市數(shù) int[,] map =newint[citiesCount, 2]; for (int i =0; i < citiesCount; i++) { string value = reader.ReadLine(); string[] temp = value.Split(''); map[i, 0] =int.Parse(temp[0]); //讀取城市坐標 map[i, 1] =int.Parse(temp[1]); } // create fitness function TSPFitnessFunction fitnessFunction =new TSPFitnessFunction(map); int populationSize = 1000; //種群最大規(guī)模 /* * 0:EliteSelection算法 * 1:RankSelection算法 * 其他:RouletteWheelSelection 算法 * */ int selectionMethod =0; // create population Population population =new Population(populationSize, new PermutationChromosome(citiesCount), fitnessFunction, (selectionMethod ==0) ? (ISelectionMethod)new EliteSelection() : (selectionMethod ==1) ? (ISelectionMethod)new RankSelection() : (ISelectionMethod)new RouletteWheelSelection() ); // iterations int iter =1; int iterations =5000; //迭代最大周期 // loop while (iter < iterations) { // run one epoch of genetic algorithm population.RunEpoch(); // increase current iteration iter++; } System.Console.WriteLine("遍歷路徑是: {0}", ((PermutationChromosome)population.BestChromosome).ToString()); System.Console.WriteLine("總路程是:{0}", fitnessFunction.PathLength(population.BestChromosome)); System.Console.Read(); } } }
網(wǎng)上據(jù)稱這組TSP數(shù)據(jù)的最好的結(jié)果是 15404 ,上面的程序我剛才試了幾次最好一次算出了15402.341,但是最差的時候也跑出了大于16000的結(jié)果。 我這還有一個版本,設(shè)置種群規(guī)模為1000,迭代5000次可以算出15408.508這個結(jié)果。源代碼在文章最后可以下載。
總結(jié)一下使用AForge.Genetic解決問題的一般步驟: (1) 定義適應函數(shù)類,需要實現(xiàn)IFitnessFunction接口 (2) 選定種群規(guī)模、使用的選擇算法、染色體種類等參數(shù),創(chuàng)建種群population (3)設(shè)定迭代的最大次數(shù),使用RunEpoch開始計算
|
|