簡介
出來工作快一年了,雖然有太多的茫然和掙扎,但很想找回曾經(jīng)那個狂熱的自己。最近想整理一下以前寫的一些小東西和文章,這是整理的第一篇,既是我的過去,也將激勵我的未來。
遺傳算法(Genetic Algorithm)是模擬達(dá)爾文生物進(jìn)化論的自然選擇和遺傳學(xué)機(jī)理的生物進(jìn)化過程的計算模型,是一種通過模擬自然進(jìn)化過程搜索最優(yōu)解的方法,它最初由美國Michigan大學(xué)J.Holland教授于1975年首先提出來的,并出版了頗有影響的專著《Adaptation in Natural and Artificial Systems》,GA這個名稱才逐漸為人所知,J.Holland教授所提出的GA通常為簡單遺傳算法(SGA),這里我就直接摘抄百度百科了。
術(shù)語
種群(population)
基因(gene)
染色體(chromosome)
表現(xiàn)型(phenotype)
編碼(codeing)
解碼(decodeing)
交叉( Crossover )
突變 ( Mutation )
基本思想
正如簡介所描述的那樣,遺傳算法是模擬達(dá)爾文的進(jìn)化論思想,我想"生存競爭,適者生存"正好簡要的闡述了這一算法的基本特質(zhì)。用適應(yīng)函數(shù)去考核每個基因的生存能力,用選擇交叉變異去實(shí)現(xiàn)進(jìn)化,搜索出種群的近似最優(yōu)解,其主要步驟如下:
1.初始化種群
2.適應(yīng)選擇
3.交叉變異
算法描述
為了更好的描述,這里我以一個01背包問題為例子來簡單的闡述遺傳算法。
給定一個背包C=100,N=5個物品,其重量和價值分別如下,求這個背包能裝的最大價值是多少
1 77 92
2 22 22
3 29 87
4 50 46
5 99 90
那么針對上面這個問題,首先我們要考慮編碼,也就是把它轉(zhuǎn)換成我們的基因型的形式,這里,我們用一個長度為5的二進(jìn)制字符串表示相應(yīng)物品的取舍。其基因型(染色體)就是10100,那么表現(xiàn)型就是選擇了1號物品和3號物品。有編碼自然就有解碼,就是把基因型轉(zhuǎn)換成表現(xiàn)型,編碼個人認(rèn)為是遺傳算法中至關(guān)重要的一步,怎么根據(jù)問題去選擇編碼方式,直接影響了后面所提到的適應(yīng)函數(shù)的簡與復(fù)雜。
把問題映射到基因型上我們已經(jīng)解決了,現(xiàn)在就是怎么去考核一個基因型對當(dāng)前環(huán)境的適應(yīng)度,換句話說就是更有可能存活遺傳下去,那么這里我們必須得設(shè)計一個適應(yīng)函數(shù)f,因?yàn)槭乔蟊嘲淖畲笾担忠驗(yàn)椴荒艹^背包所能承受的總重量,所以用h(x)表示第二個不超過總重量的條件,f(y)表示背包的價值,所以得到適應(yīng)函數(shù)f(h(x))。
然后就是怎么去選擇的問題,我們用p(x)=f(y)/totall(f(y))來表示某個基因型占總體的概率,現(xiàn)在我們就采用輪盤賭的方法,什么是輪盤賭呢,這里簡要提及一下,我們把各個概率放在一個輪盤里,然后轉(zhuǎn)動這個輪盤,最后輪盤停止,指針停在哪里就代表選擇哪個概率的事件。
比如在01背包中,我們根據(jù)適應(yīng)函數(shù)算出各個基因型的適應(yīng)度,也算出了每個基因型所占的比例,然后我們根據(jù)輪盤賭的方法從中選擇出n條基因型出來,然后n條基因型隨機(jī)兩兩配對交叉產(chǎn)生兩個子代。
交叉
那么什么是交叉呢,和生物學(xué)中染色體交叉是一樣的概念,就是互換某一段基因,如下圖,這里我們采用的是單點(diǎn)交叉。
變異
變異是指,在某一個比較小的概率下,某個基因在遺傳時,某個基因座上的基因由0邊成1,或者由1變成0。
新產(chǎn)生的基因型,如果原來的種群中沒有的話,就加進(jìn)這個種群,然后再按上面所述去迭代,直到找到我們比較滿意的基因型為止,現(xiàn)在我們什么都準(zhǔn)備好了,就讓它們?nèi)ソ慌浒眩?chuàng)建一個種族吧。
下面給出按照上面的例子我臨時打的代碼.
package artiano.ga;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
class Chromosome{
String key;
double f;
double p;
public Chromosome(String key,double f,double p){
this.key=key;
this.f=f;
this.p=p;
}
public Chromosome(Chromosome o){
this.key=o.key;
this.f=o.f;
this.p=o.p;
}
}
public class GAtest {
public Map<String,Chromosome> SAVE=new HashMap<String,Chromosome>();
public List<Chromosome> list;
public double[][] B={
{77,92},
{22,22},
{29,87},
{50,46},
{99,90}
};
private double M=100;
private int N=5;
public double toPhenotype(String key){ //解碼成表現(xiàn)型
double count=0;
for(int i=0;i<key.length();i++){
if(key.charAt(i)=='1'){
count+=B[i][1];
}
}
return count;
}
public void init(int n){ //這里的初始化種族應(yīng)該是隨機(jī)的,這里為了簡單起見,我隨便給了一組
SAVE.put("10000", new Chromosome("10000",0,0));
SAVE.put("01100", new Chromosome("01100",0,0));
SAVE.put("00001", new Chromosome("00001",0,0));
SAVE.put("01010", new Chromosome("01010",0,0));
}
public String lunpandu(){ //輪盤賭
double nowP=Math.random();
Set<String> keySet=SAVE.keySet();
Iterator it=keySet.iterator();
double m=0;
while(it.hasNext()){
String key=(String)it.next();
Chromosome o=SAVE.get(key);
m+=o.p;
if(nowP<=m) return key;
}
return "";
}
public Chromosome selected(){ //選擇
Set<String> keySet=SAVE.keySet();
Iterator it=keySet.iterator();
double count=0;
Chromosome max=new Chromosome("-1",0,0);
while(it.hasNext()){
String key=(String)it.next();
Chromosome o=SAVE.get(key);
count+=o.f=toPhenotype(key);
if(o.f>max.f) max=o;
}
it=keySet.iterator();
while(it.hasNext()){
String key=(String)it.next();
Chromosome o=SAVE.get(key);
o.p=o.f/count;
System.out.println(o.key+" "+o.f+" "+o.p);
}
list=new LinkedList<Chromosome>();
for(int i=0;i<2;i++){
String seleKey=lunpandu();
list.add(new Chromosome(SAVE.get(seleKey)));
System.out.println("--->"+seleKey);
}
return max;
}
public void crossover(){ //交叉
for(int i=0;i<list.size()/2;i++){
Chromosome o1=list.get(i*2);
Chromosome o2=list.get(i*2+1);
int index=(int)Math.round(1+Math.random() * 2);
String o11=o1.key.substring(0, index);
String o12=o1.key.substring(index, o1.key.length());
String o21=o2.key.substring(0, index);
String o22=o2.key.substring(index, o1.key.length());
System.out.println("|||| "+o11+" | "+o12);
System.out.println("|||| "+o21+" | "+o22);
o1.key=o11+o22;
o2.key=o21+o12;
System.out.println("|||| "+o1.key);
System.out.println("|||| "+o2.key);
long bianyi=Math.round(Math.random() * 10000);
if(bianyi<100);
if(hefa(o1.key) && SAVE.get(o1.key)==null) SAVE.put(o1.key, o1);
if(hefa(o2.key) && SAVE.get(o2.key)==null) SAVE.put(o2.key, o2);
}
}
public boolean hefa(String key){ //是否滿足h(x)
double m=0;
for(int i=0;i<N;i++){
if(key.charAt(i)=='1'){
m+=B[i][0];
}
}
if(m<=M)return true;
return false;
}
public void iteration(int n){ //種群迭代
for(int i=0;i<n;i++){
System.out.println("========="+(i+1));
Chromosome max=selected();
if(max.f>=133){
System.out.println(" [----"+max.key+" "+max.f+" "+max.p+"-----]");
break;
}
crossover();
}
}
public static void main(String[] args){
GAtest ts=new GAtest();
ts.init(6);
ts.iteration(510);
}
}
打印結(jié)果:
=========1
00001 90.0 0.25069637883008355
10000 92.0 0.2562674094707521
01010 68.0 0.1894150417827298
01100 109.0 0.30362116991643456
--->01100
--->01100
|||| 01 | 100
|||| 01 | 100
|||| 01100
|||| 01100
=========2
00001 90.0 0.25069637883008355
10000 92.0 0.2562674094707521
01010 68.0 0.1894150417827298
01100 109.0 0.30362116991643456
--->01100
--->01100
|||| 0 | 1100
|||| 0 | 1100
|||| 01100
|||| 01100
=========3
00001 90.0 0.25069637883008355
10000 92.0 0.2562674094707521
01010 68.0 0.1894150417827298
01100 109.0 0.30362116991643456
--->10000
--->00001
|||| 10 | 000
|||| 00 | 001
|||| 10001
|||| 00000
=========4
00000 0.0 0.0
00001 90.0 0.25069637883008355
10000 92.0 0.2562674094707521
01010 68.0 0.1894150417827298
01100 109.0 0.30362116991643456
--->01100
--->00001
|||| 011 | 00
|||| 000 | 01
|||| 01101
|||| 00000
=========5
00000 0.0 0.0
00001 90.0 0.25069637883008355
10000 92.0 0.2562674094707521
01010 68.0 0.1894150417827298
01100 109.0 0.30362116991643456
--->00001
--->10000
|||| 00 | 001
|||| 10 | 000
|||| 00000
|||| 10001
=========6
00000 0.0 0.0
00001 90.0 0.25069637883008355
10000 92.0 0.2562674094707521
01010 68.0 0.1894150417827298
01100 109.0 0.30362116991643456
--->00001
--->01100
|||| 00 | 001
|||| 01 | 100
|||| 00100
|||| 01001
=========7
00000 0.0 0.0
00001 90.0 0.20179372197309417
10000 92.0 0.2062780269058296
01010 68.0 0.15246636771300448
00100 87.0 0.19506726457399104
01100 109.0 0.24439461883408073
--->01100
--->01010
|||| 0 | 1100
|||| 0 | 1010
|||| 01010
|||| 01100
=========8
00000 0.0 0.0
00001 90.0 0.20179372197309417
10000 92.0 0.2062780269058296
01010 68.0 0.15246636771300448
00100 87.0 0.19506726457399104
01100 109.0 0.24439461883408073
--->10000
--->01100
|||| 10 | 000
|||| 01 | 100
|||| 10100
|||| 01000
=========9
00000 0.0 0.0
00001 90.0 0.19230769230769232
10000 92.0 0.19658119658119658
01000 22.0 0.04700854700854701
01010 68.0 0.1452991452991453
00100 87.0 0.1858974358974359
01100 109.0 0.2329059829059829
--->00100
--->00100
|||| 0 | 0100
|||| 0 | 0100
|||| 00100
|||| 00100
=========10
00000 0.0 0.0
00001 90.0 0.19230769230769232
10000 92.0 0.19658119658119658
01000 22.0 0.04700854700854701
01010 68.0 0.1452991452991453
00100 87.0 0.1858974358974359
01100 109.0 0.2329059829059829
--->10000
--->01010
|||| 10 | 000
|||| 01 | 010
|||| 10010
|||| 01000
=========11
00000 0.0 0.0
00001 90.0 0.19230769230769232
10000 92.0 0.19658119658119658
01000 22.0 0.04700854700854701
01010 68.0 0.1452991452991453
00100 87.0 0.1858974358974359
01100 109.0 0.2329059829059829
--->01100
--->01100
|||| 01 | 100
|||| 01 | 100
|||| 01100
|||| 01100
=========12
00000 0.0 0.0
00001 90.0 0.19230769230769232
10000 92.0 0.19658119658119658
01000 22.0 0.04700854700854701
01010 68.0 0.1452991452991453
00100 87.0 0.1858974358974359
01100 109.0 0.2329059829059829
--->00001
--->10000
|||| 000 | 01
|||| 100 | 00
|||| 00000
|||| 10001
=========13
00000 0.0 0.0
00001 90.0 0.19230769230769232
10000 92.0 0.19658119658119658
01000 22.0 0.04700854700854701
01010 68.0 0.1452991452991453
00100 87.0 0.1858974358974359
01100 109.0 0.2329059829059829
--->00100
--->00001
|||| 001 | 00
|||| 000 | 01
|||| 00101
|||| 00000
=========14
00000 0.0 0.0
00001 90.0 0.19230769230769232
10000 92.0 0.19658119658119658
01000 22.0 0.04700854700854701
01010 68.0 0.1452991452991453
00100 87.0 0.1858974358974359
01100 109.0 0.2329059829059829
--->00100
--->00100
|||| 00 | 100
|||| 00 | 100
|||| 00100
|||| 00100
=========15
00000 0.0 0.0
00001 90.0 0.19230769230769232
10000 92.0 0.19658119658119658
01000 22.0 0.04700854700854701
01010 68.0 0.1452991452991453
00100 87.0 0.1858974358974359
01100 109.0 0.2329059829059829
--->10000
--->01100
|||| 1 | 0000
|||| 0 | 1100
|||| 11100
|||| 00000
=========16
00000 0.0 0.0
00001 90.0 0.19230769230769232
10000 92.0 0.19658119658119658
01000 22.0 0.04700854700854701
01010 68.0 0.1452991452991453
00100 87.0 0.1858974358974359
01100 109.0 0.2329059829059829
--->00001
--->00100
|||| 00 | 001
|||| 00 | 100
|||| 00100
|||| 00001
=========17
00000 0.0 0.0
00001 90.0 0.19230769230769232
10000 92.0 0.19658119658119658
01000 22.0 0.04700854700854701
01010 68.0 0.1452991452991453
00100 87.0 0.1858974358974359
01100 109.0 0.2329059829059829
--->01010
--->00100
|||| 010 | 10
|||| 001 | 00
|||| 01000
|||| 00110
=========18
00000 0.0 0.0
00001 90.0 0.1497504159733777
10000 92.0 0.15307820299500832
01000 22.0 0.036605657237936774
01010 68.0 0.11314475873544093
00110 133.0 0.22129783693843594
00100 87.0 0.1447587354409318
01100 109.0 0.18136439267886856
--->00001
--->01100
[----00110 133.0 0.22129783693843594-----]
最后
正如我們看見的那樣,遺傳算法,每次迭代都會從整個集合中去計算選擇,然后通過交叉變異去產(chǎn)生新的種群,這樣的好處顯而易見,不會像梯度下降,貪心這類算法會陷入局部最優(yōu),至于其編碼的選擇,我想編碼的好壞決定了適應(yīng)函數(shù)的簡單還是復(fù)雜的程度,適應(yīng)函數(shù)設(shè)計的好壞決定了,整個算法是否能更快更好的收斂于近似最優(yōu)。注意這里,遺傳算法不能保證會得到最優(yōu)解,但是在整個迭代過程,它會不斷的接近于最優(yōu)。
值得一提的是,遺傳算法是基于圖式理論的,其基因型從各個特征進(jìn)行描述,這樣就隱含了其搜索過程是并行處理的,對于隱含并行能力,現(xiàn)在已經(jīng)給出了證明,每次迭代,對于n的種群,大致處理了O(n^3)個狀態(tài),這個處理能力還是相當(dāng)吃驚的。