自話遺傳算法(帶實(shí)例)

簡介

出來工作快一年了,雖然有太多的茫然和掙扎,但很想找回曾經(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)吃驚的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容