遺傳算法解TSP(2)-遺傳算子

引言

上一章講解了遺傳算法的基本思想與工作流程,構(gòu)建了物種類Species及評(píng)價(jià)物種優(yōu)劣的適應(yīng)度函數(shù)。本章敘述如何利用求得的物種適應(yīng)度去進(jìn)行優(yōu)勝劣汰的“選擇算子”、物種間繁衍配對(duì)的“交叉算子”以及單個(gè)物種基因突變的“變異算子”。

選擇算子

1.選擇概率

物種si(i=1,2,…,n)的適應(yīng)度f(wàn)(si)已然得到,接下來(lái)就要利用f(si),求得它在整個(gè)種群中被選擇(即遺傳到下一代)的概率。這個(gè)概率表示為:



產(chǎn)生了概率以后,我們便需要進(jìn)行選擇。

2.輪盤賭選擇法

采用“輪盤賭選擇法”,形象點(diǎn)說,就像我們經(jīng)常見到的抽獎(jiǎng)轉(zhuǎn)轉(zhuǎn)盤一樣:



即每次篩選就相當(dāng)于轉(zhuǎn)動(dòng)輪盤,概率大的面積就越大,自然就更容易被選上。那么“轉(zhuǎn)”多少次呢?這里就涉及種群容量的約定了,我們?nèi)绻x得過小,那么物種的多樣性不夠,很難有機(jī)會(huì)產(chǎn)生更優(yōu)秀的物種(就像如果地球上其他生物都滅絕了,只剩人類,那么物種改變的機(jī)會(huì),即路線更短的概率相對(duì)就越小了),而如果過大,那么算法的效率會(huì)降低,隨機(jī)性更大,最后很不容易收斂。而根據(jù)一些文章和經(jīng)驗(yàn),每一輪我們就維持上一輪的種群容量大小即可,保持種群總量不變,即由初始種群的大小決定。那初始種群大小又選多大呢?這個(gè)參數(shù)就需要根據(jù)具體問題的規(guī)模來(lái)進(jìn)行調(diào)整了,如果城市數(shù)很少,可以適當(dāng)小一些,如果很多,就調(diào)大點(diǎn)。

3.選擇過程

下面舉一個(gè)實(shí)際例子來(lái)說明具體怎么選擇。假設(shè)我們初始化了這樣一個(gè)種群,也計(jì)算了他們的適應(yīng)度、選擇概率,得到如下的表:



從上表很容易看出:物種2因?yàn)槁肪€較短所以適應(yīng)度高,進(jìn)而經(jīng)過4次輪賭直接被選中了2次,而較次的物種1被選中了1次,物種3雖然適應(yīng)度比物種4高,但由于算法的隨機(jī)性并沒有被選上,而是物種4“僥幸”被選上了。所以新的種群應(yīng)該是這樣的:一個(gè)物種1、兩個(gè)物種2和一個(gè)物種4,即種群基因?yàn)椋?25367498、325974681、325974681和974613528。

4.精英保留策略

這里我們不難發(fā)現(xiàn)一個(gè)問題:倘若物種2的選中概率沒有51.54%那么高,而是稍低一些(保證仍是種群中適應(yīng)度最高的物種),那么這時(shí)它參與輪賭被選中的次數(shù)就難說有2次了,而很可能多多地讓那些資質(zhì)現(xiàn)在并非很好的物種1、3、4遺傳到了下一代。但物種2仍是適應(yīng)度最高的精英物種啊!讓它懷才不遇,最后落得個(gè)滄海遺珠之憾,的確有失公平。
所以這時(shí),我們加入一個(gè)“精英保留策略”,即并非所有物種均參與賭輪,而是在輪賭之前,先選出適應(yīng)度最高的那個(gè)物種,復(fù)制若干個(gè)后提前進(jìn)入下一代,接著再讓剩余的物種參與賭輪進(jìn)入下一代,最終兩部分合成一個(gè)新種群。這樣避免了因?yàn)楦怕试颍沟脙?yōu)秀物種滄海遺珠的情況發(fā)生,但這樣做也容易陷入局部最優(yōu),所以多少個(gè)精英這個(gè)參數(shù)就需要不斷地調(diào)整了,據(jù)一些研究經(jīng)驗(yàn)來(lái)看,一般復(fù)制1/4效果是比較好的。
下面是整個(gè)選擇算子的實(shí)現(xiàn)代碼:

//選擇優(yōu)秀物種(輪盤賭)
void select(List<SpeciesNode> speciesList)
{            
    //計(jì)算選擇概率
    calRate(speciesList);
    
    //找出最大適應(yīng)度物種
    float talentFitness=Float.MAX_VALUE;
    SpeciesNode talentSpecies=null;
    for(SpeciesNode species : speciesList)
    {
        if(species.fitness < talentFitness)
        {
            talentFitness = species.fitness;
            talentSpecies = species;
        }
    }
    //將最大適應(yīng)度物種復(fù)制talentNum個(gè)
    List<SpeciesNode> newSpeciesList=new ArrayList<SpeciesNode>();
    int talentNum = (int)(speciesList.size() * tp); //tp:復(fù)制比重
    for(int i=1;i<=talentNum;i++)
    {
        //復(fù)制物種至新表
        SpeciesNode newSpecies=talentSpecies.clone();
        newSpeciesList.add(newSpecies);
    }
    //輪盤賭speciesList.speciesNum-talentNum次
    int roundNum=speciesList.size()-talentNum;
    for(int i=1;i<=roundNum;i++)
    {
        //產(chǎn)生0-1的概率
        float rate=(float)Math.random();
        for(SpeciesNode species : speciesList)
        {
            if(species == talentSpecies || rate > species.rate) //精英物種或未選中,跳過
                rate=rate-species.rate;
            else //該物種在本次輪賭中選中
            {
                SpeciesNode newSpecies=species.clone();
                newSpeciesList.add(newSpecies);        
                break;
            }
        }        
    }
    speciesList = newSpeciesList;
}

交叉算子

交叉是對(duì)種群內(nèi)兩物種的基因序列進(jìn)行裁剪組合的操作,一般以一定概率執(zhí)行,而不是每次都執(zhí)行。物種的配對(duì)選取最好隨機(jī),而不要1和2配對(duì),3和4配對(duì)(因?yàn)樵谑褂镁⒈A舨呗詴r(shí)很可能是連續(xù)追加進(jìn)種群的,這樣相當(dāng)于近親繁殖,很難擦出火花即產(chǎn)生路線長(zhǎng)度比雙親都短的后代基因)。那么雙親的基因序列之間具體怎么交叉呢?
由于物種基因的編碼形式是以“城市編號(hào)”為元素的,在實(shí)現(xiàn)交叉操作時(shí)首先任選一個(gè)位置作為起點(diǎn),交換兩個(gè)物種的后半段基因。但需注意的是,交叉后的后半段基因可能與物種的前半段基因重復(fù),故而還需進(jìn)行基因沖突處理,即把物種1所有重復(fù)的基因與物種2所有重復(fù)的基因?qū)?yīng)交換。交叉操作具體如下圖所示:



具體實(shí)現(xiàn)代碼如下:

//交叉操作
void crossover(List<SpeciesNode> speciesList)
{
    for(int n=0;n<speciesList.size();n+=2) //兩兩配對(duì)
    {
        if(n+1 >= speciesList.size()) //已無(wú)可配對(duì)的母物種(種群容量為奇數(shù))
            break; //結(jié)束
        
        SpeciesNode fatherSpecies = speciesList.get(n); //父物種
        SpeciesNode motherSpecies = speciesList.get(n+1); //母物種
        
        //交叉概率 pcl < rate < pch
        float rate=(float)Math.random();
        if(rate > Constant.pcl && rate < Constant.pch) 
        {            
            int crossPosition=rand.nextInt(Constant.CITY_NUM); //隨機(jī)生成交叉點(diǎn)
            List<Integer> fatherDuplicateGenesList = new ArrayList<Integer>(); // 存儲(chǔ)父物種前半段所有重復(fù)基因的位置
            List<Integer> motherDuplicateGenesList = new ArrayList<Integer>(); // 存儲(chǔ)母物種前半段所有重復(fù)基因的位置
            
            //后半段基因挨個(gè)位置進(jìn)行互換
            for(int i=crossPosition;i<Constant.CITY_NUM;i++)
            {
                //基因互換
                int gene;
                gene=fatherSpecies.genes[i];
                fatherSpecies.genes[i]=motherSpecies.genes[i];
                motherSpecies.genes[i]=gene;
                
                //檢測(cè)fatherSpecies.genes[i]是否與父物種前半段某位置基因重復(fù),若是則記錄
                for(int j=0;j<crossPosition;j++)
                    if(fatherSpecies.genes[i] == fatherSpecies.genes[j])
                        fatherDuplicateGenesList.add(j);
                
                //母物種同理
                for(int j=0;j<crossPosition;j++)
                    if(motherSpecies.genes[i] == motherSpecies.genes[j])
                        motherDuplicateGenesList.add(j);
            }
            
            //去重
            for(int k=0;k<fatherDuplicateGenesList.size();k++)
            {
                //父、母物種前半段重復(fù)的基因?qū)?yīng)交換
                int fatherGenePosition = fatherDuplicateGenesList.get(k);
                int motherGenePosition = motherDuplicateGenesList.get(k);
                
                int gene;
                gene=fatherSpecies.genes[fatherGenePosition];
                fatherSpecies.genes[fatherGenePosition]=motherSpecies.genes[motherGenePosition];
                motherSpecies.genes[i]=gene;
            }
        }
    }
}

變異算子

“變異”是跳出局部最優(yōu)解的一個(gè)重要法寶,是對(duì)基因序列進(jìn)行若干變換的一種操作,一般按非常小的概率執(zhí)行。本文采用的是一種學(xué)界普遍認(rèn)為效果較好的一種變異方式,即隨機(jī)選取基因序列的兩個(gè)位置k和m,逆轉(zhuǎn)其k~m間的城市編號(hào),即若現(xiàn)有物種:



隨機(jī)產(chǎn)生1和n之間的兩相異整數(shù)k和m,若k<m,執(zhí)行逆轉(zhuǎn)變換,得到新的基因序列:



下面是代碼:
//變異操作
void mutate(List<SpeciesNode> speciesList)
{    
    //每一物種均有變異的機(jī)會(huì),以概率pm進(jìn)行
    for(SpeciesNode species : speciesList)
    {        
        float rate=(float)Math.random();
        if(rate < Constant.pm)
        {
            //尋找逆轉(zhuǎn)的左右端點(diǎn)
            Random rand=new Random();
            int left=rand.nextInt(Constant.CITY_NUM);
            int right=rand.nextInt(Constant.CITY_NUM);
            if(left > right)
            {
                int tmp;
                tmp=left;
                left=right;
                right=tmp;
            }
            
            //逆轉(zhuǎn)left-right下標(biāo)元素
            while(left < right)
            {
                int tmp;
                tmp=species.genes[left];
                species.genes[left]=species.genes[right];
                species.genes[right]=tmp;
                left++;
                right--;
            }
        }
    }
}

結(jié)語(yǔ)

啊~累死了,遺傳算法求解TSP的基本思想差不多就是這些啦。下一章將給出GeneticAlgorithm類的一個(gè)總控調(diào)用流程、遺傳算法的一些常量參數(shù)定義及算法的實(shí)際運(yùn)行效果。

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

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