基因算法與aforge.net

遺傳算法 ( GA , Genetic Algorithm ) ,也稱進化算法 。 遺傳算法是受達爾文的進化論的啓發,借鑑生物進化過程而提出的一種啓發式搜索算法。因此在介紹遺傳算法前有必要簡單的介紹生物進化知識。

 

 

一.進化論知識

  作爲遺傳算法生物背景的介紹,下面內容瞭解即可:

  種羣(Population)生物的進化以羣體的形式進行,這樣的一個羣體稱爲種羣。

  個體:組成種羣的單個生物。

  基因 ( Gene ) 一個遺傳因子。

  染色體 ( Chromosome ) :包含一組的基因。

  生存競爭,適者生存:對環境適應度高的、牛B的個體參與繁殖的機會比較多,後代就會越來越多。適應度低的個體參與繁殖的機會比較少,後代就會越來越少。

  遺傳與變異:新個體會遺傳父母雙方各一部分的基因,同時有一定的概率發生基因變異。

 

  簡單說來就是:繁殖過程,會發生基因交叉( Crossover ) ,基因突變 ( Mutation ) ,適應度( Fitness )低的個體會被逐步淘汰,而適應度高的個體會越來越多。那麼經過N代的自然選擇後,保存下來的個體都是適應度很高的,其中很可能包含史上產生的適應度最高的那個個體。

 

 

二.遺傳算法思想

  借鑑生物進化論,遺傳算法將要解決的問題模擬成一個生物進化的過程,通過複製、交叉、突變等操作產生下一代的解,並逐步淘汰掉適應度函數值低的解,增加適應度函數值高的解。這樣進化N代後就很有可能會進化出適應度函數值很高的個體。

  舉個例子,使用遺傳算法解決“0-1揹包問題”的思路:0-1揹包的解可以編碼爲一串0-1字符串(0:不取,1:取) ;首先,隨機產生M個0-1字符串,然後評價這些0-1字符串作爲0-1揹包問題的解的優劣;然後,隨機選擇一些字符串通過交叉、突變等操作產生下一代的M個字符串,而且較優的解被選中的概率要比較高。這樣經過G代的進化後就可能會產生出0-1揹包問題的一個“近似最優解”。

 

  編碼:需要將問題的解編碼成字符串的形式才能使用遺傳算法。最簡單的一種編碼方式是二進制編碼,即將問題的解編碼成二進制位數組的形式。例如,問題的解是整數,那麼可以將其編碼成二進制位數組的形式。將0-1字符串作爲0-1揹包問題的解就屬於二進制編碼。

 

  遺傳算法有3個最基本的操作:選擇,交叉,變異。

 

  選擇:選擇一些染色體來產生下一代。一種常用的選擇策略是 “比例選擇”,也就是個體被選中的概率與其適應度函數值成正比。假設羣體的個體總數是M,那麼那麼一個體Xi被選中的概率爲f(Xi)/( f(X1) + f(X2) + …….. + f(Xn) ) 。比例選擇實現算法就是所謂的“輪盤賭算法”( Roulette Wheel Selection ) ,輪盤賭算法的一個簡單的實現如下:

 

輪盤賭算法
/*
* 按設定的概率,隨機選中一個個體
* P[i]表示第i個個體被選中的概率
*/
int RWS()
{
m
= 0;
r
=Random(0,1); //r爲0至1的隨機數
for(i=1;i<=N; i++)
{
/* 產生的隨機數在m~m+P[i]間則認爲選中了i
* 因此i被選中的概率是P[i]
*/
m
= m + P[i];
if(r<=m) return i;
}
}

 

交叉(Crossover):2條染色體交換部分基因,來構造下一代的2條新的染色體。例如:

交叉前:

00000|011100000000|10000

11100|000001111110|00101

交叉後:

00000|000001111110|10000

11100|011100000000|00101

染色體交叉是以一定的概率發生的,這個概率記爲Pc 。

 

變異(Mutation):在繁殖過程,新產生的染色體中的基因會以一定的概率出錯,稱爲變異。變異發生的概率記爲Pm 。例如:

變異前:

000001110000000010000

變異後:

000001110000100010000

 

適應度函數 ( Fitness Function ):用於評價某個染色體的適應度,用f(x)表示。有時需要區分染色體的適應度函數與問題的目標函數。例如:0-1揹包問題的目標函數是所取得物品價值,但將物品價值作爲染色體的適應度函數可能並不一定適合。適應度函數與目標函數是正相關的,可對目標函數作一些變形來得到適應度函數。

 

 

三.基本遺傳算法的僞代碼

 

 

基本遺傳算法僞代碼
/*
* Pc:交叉發生的概率
* Pm:變異發生的概率
* M:種羣規模
* G:終止進化的代數
* Tf:進化產生的任何一個個體的適應度函數超過Tf,則可以終止進化過程
*/
初始化Pm,Pc,M,G,Tf等參數。隨機產生第一代種羣Pop

do
{
  計算種羣Pop中每一個體的適應度F(i)。
  初始化空種羣newPop
  do
  {
    根據適應度以比例選擇算法從種羣Pop中選出2個個體
    if ( random ( 0 , 1 ) < Pc )
    {
      對2個個體按交叉概率Pc執行交叉操作
    }
    if ( random ( 0 , 1 ) < Pm )
    {
      對2個個體按變異概率Pm執行變異操作
    }
將2個新個體加入種羣newPop中
} until ( M個子代被創建 )
用newPop取代Pop
}until ( 任何染色體得分超過Tf, 或繁殖代數超過G )

 

 

四.基本遺傳算法優化

  下面的方法可優化遺傳算法的性能。

  精英主義(Elitist Strategy)選擇:是基本遺傳算法的一種優化。爲了防止進化過程中產生的最優解被交叉和變異所破壞,可以將每一代中的最優解原封不動的複製到下一代中。

  插入操作:可在3個基本操作的基礎上增加一個插入操作。插入操作將染色體中的某個隨機的片段移位到另一個隨機的位置。



五. 使用AForge.Genetic解決TSP問題

  AForge.NET是一個C#實現的面向人工智能、計算機視覺等領域的開源架構。AForge.NET中包含有一個遺傳算法的類庫。

 

  AForge.NET主頁:http://www.aforgenet.com/

  AForge.NET代碼下載:http://code.google.com/p/aforge/

 

  介紹一下AForge的遺傳算法用法吧。AForge.Genetic的類結構如下:


圖1. AForge.Genetic的類圖

 

 

下面用AForge.Genetic寫個解決TSP問題的最簡單實例。測試數據集採用網上流傳的中國31個省會城市的座標:

 

1304 2312
3639 1315
4177 2244
3712 1399
3488 1535
3326 1556
3238 1229
4196 1004
4312 790
4386 570
3007 1970
2562 1756
2788 1491
2381 1676
1332 695
3715 1678
3918 2179
4061 2370
3780 2212
3676 2578
4029 2838
4263 2931
3429 1908
3507 2367
3394 2643
3439 3201
2935 3240
3140 3550
2545 2357
2778 2826
2370 2975

 

 

 

操作過程:

(1) 下載AForge.NET類庫,網址:http://code.google.com/p/aforge/downloads/list

(2) 創建C#空項目GenticTSP。然後在AForge目錄下找到AForge.dll和AForge.Genetic.dll,將其拷貝到TestTSP項目的bin/Debug目錄下。再通過“Add Reference...”將這兩個DLL添加到工程。

(3) 將31個城市座標數據保存爲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>
public class TSPFitnessFunction : IFitnessFunction
{
// map
private int[,] map = null;

// Constructor
public TSPFitnessFunction(int[,] map)
{
this.map = map;
}

/// <summary>
/// Evaluate chromosome - calculates its fitness value
/// </summary>
public double Evaluate(IChromosome chromosome)
{
return 1 / (PathLength(chromosome) + 1);
}

/// <summary>
/// Translate genotype to phenotype
/// </summary>
public object Translate(IChromosome chromosome)
{
return chromosome.ToString();
}

/// <summary>
/// Calculate path length represented by the specified chromosome
/// </summary>
public double PathLength(IChromosome chromosome)
{
// salesman path
ushort[] path = ((PermutationChromosome)chromosome).Value;

// check path size
if (path.Length != map.GetLength(0))
{
throw new 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
{

static void Main()
{
StreamReader reader
= new StreamReader("Data.txt");

int citiesCount = 31; //城市數

int[,] map = new int[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; //種羣最大規模

/*
* 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();

}
}
}

 

 

 

網上據稱這組TSP數據的最好的結果是 15404 ,上面的程序我剛纔試了幾次最好一次算出了15402.341,但是最差的時候也跑出了大於16000的結果。

我這還有一個版本,設置種羣規模爲1000,迭代5000次可以算出15408.508這個結果。源代碼在文章最後可以下載。

 

總結一下使用AForge.Genetic解決問題的一般步驟:

(1) 定義適應函數類,需要實現IFitnessFunction接口

(2) 選定種羣規模、使用的選擇算法、染色體種類等參數,創建種羣population

(3)設定迭代的最大次數,使用RunEpoch開始計算

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