遺傳算法之Python實(shí)現(xiàn)

遺傳算法之Python實(shí)現(xiàn)

寫在前面

之前的文章中已經(jīng)講過了遺傳算法的基本流程,并且用MATLAB實(shí)現(xiàn)過一遍了。這一篇文章主要面對的人群是看過了我之前的文章,因此我就不再贅述遺傳算法是什么以及基本的內(nèi)容了,假設(shè)大家已經(jīng)知道我是怎么寫遺傳算法的了。

Python的遺傳算法主函數(shù)

我的思想是,創(chuàng)建一個(gè)染色體的類,其中包括了兩個(gè)變量:染色體chrom與適應(yīng)度fitness。因此我們就可以通過直接建立對象來作為種群中的個(gè)體。

#染色體的類
class Chrom:
    chrom = []
    fitness = 0
    def showChrom(self):
        print(self.chrom)
    def showFitness(self):
        print(self.fitness)

所以我們開始設(shè)置基礎(chǔ)參數(shù)。其中種群的表達(dá)方式我用的是字典,也就是用一個(gè)字典來保存種群內(nèi)的所有個(gè)體,這個(gè)也是我想出來的創(chuàng)建多個(gè)對象的方法。

將字典的索引為個(gè)體的標(biāo)號(hào),如:chrom1, chrom2等。字典索引的值就是一個(gè)對象。這個(gè)對象擁有兩個(gè)屬性,就是染色體與適應(yīng)度。

其實(shí)在這一方便來說,我覺得在思路上是優(yōu)于利用MATLAB的矩陣式編程的。因?yàn)檫@樣可以很直觀的將個(gè)體與個(gè)體的屬性這一種思想給表達(dá)出來,相比一堆矩陣來說,在邏輯上比較容易接受。

#基礎(chǔ)參數(shù)
N = 200  #種群內(nèi)個(gè)體數(shù)目
mut = 0.2  #突變概率
acr = 0.2  #交叉概率

pop = {}  #存儲(chǔ)染色體的字典
for i in range(N):
    pop['chrom'+str(i)] = Chrom()
chromNodes = 2  #染色體節(jié)點(diǎn)數(shù)(變量個(gè)數(shù))
iterNum = 10000  #迭代次數(shù)
chromRange = [[0, 10], [0, 10]]  #染色體范圍
aveFitnessList = []  #平均適應(yīng)度
bestFitnessList = []  #最優(yōu)適應(yīng)度

之后就是初始染色體了,其中就牽扯到了各種用來初始化種群、計(jì)算適應(yīng)度、找最優(yōu)等函數(shù),我在這里分出了兩個(gè)文件,分別為Genetic.py與Fitness.py。

Genetic.py里面有八個(gè)函數(shù),主要包含了作用于種群或者染色體操作的函數(shù),分別為:

  • findBest函數(shù),用于尋找種群中的最優(yōu)染色體;
  • findworse函數(shù),用于尋找種群中的最劣染色體;
  • initialize函數(shù),用于初始化種群;
  • calAveFitness函數(shù),用于計(jì)算種群的平均適應(yīng)度;
  • mutChrom函數(shù),用于對染色體進(jìn)行變異;
  • inRange函數(shù),用于判斷染色體節(jié)點(diǎn)值是否越界;
  • acrChrom函數(shù),用于對染色體進(jìn)行交叉;
  • compareChrom函數(shù),用于比較兩個(gè)染色體孰優(yōu)孰劣。

Fitness.py里面有兩個(gè)函數(shù),主要包含了對適應(yīng)度操作的函數(shù),分別為:

  • calFitness函數(shù),用來迭代每一個(gè)個(gè)體,并計(jì)算適應(yīng)度(利用funcFitness函數(shù)計(jì)算);
  • funcFitness函數(shù),計(jì)算單個(gè)個(gè)體的適應(yīng)度。

因此可以列出初始化代碼為

#初始染色體
pop = Genetic.initialize(pop, chromNodes, chromRange)
pop = Fitness.calFitness(pop)  #計(jì)算適應(yīng)度
bestChrom = Genetic.findBest(pop)  #尋找最優(yōu)染色體
bestFitnessList.append(bestChrom[1])  #將當(dāng)前最優(yōu)適應(yīng)度壓入列表中
aveFitnessList.append(Genetic.calAveFitness(pop, N))  #計(jì)算并存儲(chǔ)平均適應(yīng)度

迭代過程的思路和邏輯與MATLAB無異

#開始迭代
for t in range(iterNum):
    #染色體突變
    pop = Genetic.mutChrom(pop, mut, chromNodes, bestChrom, chromRange)
    #染色體交換
    pop = Genetic.acrChrom(pop, acr, chromNodes)
    #尋找最優(yōu)
    nowBestChrom = Genetic.findBest(pop)
    #比較前一個(gè)時(shí)間的最優(yōu)和現(xiàn)在的最優(yōu)
    bestChrom = Genetic.compareChrom(nowBestChrom, bestChrom)
    #尋找與替換最劣
    worseChrom = Genetic.findWorse(pop)
    pop[worseChrom[0]].chrom = pop[bestChrom[0]].chrom.copy()
    pop[worseChrom[0]].fitness = pop[bestChrom[0]].fitness
    #存儲(chǔ)最優(yōu)與平均
    bestFitnessList.append(bestChrom[1])
    aveFitnessList.append(Genetic.calAveFitness(pop, N))

最后再做一下迭代的的圖像

plt.figure(1)
plt.plot(x, aveFitnessList)
plt.plot(x, bestFitnessList)
plt.show()

最后再在最前面加上各種庫和文件就可以運(yùn)行了。

import Genetic
import Fitness
import matplotlib.pyplot as plt
import numpy as np

感悟

可以說最主要的感悟就是染色體這一個(gè)類。其實(shí)那個(gè)Genetic.py與Fitness.py這兩個(gè)文件也可以直接包裝成類,但是這樣一來我就嫌主文件太臃腫,在其他里面再包裝成類又多此一舉,畢竟這只是一個(gè)小程序,所以我就這樣寫了。

深刻感悟到了面向?qū)ο缶幊痰膬?yōu)點(diǎn),在編程邏輯的處理上真是一種享受,只需要思考對象的屬性即可,省去了許多復(fù)雜的思考。

另一個(gè)感悟就是創(chuàng)建多個(gè)對象時(shí),利用字典的方法來創(chuàng)建對象。當(dāng)初我也是困惑怎么建立一個(gè)類似于C++中的對象數(shù)組,上網(wǎng)查找了各種方法,結(jié)果都避而不談(當(dāng)然,也可能是我搜索能力太差沒找到),所以經(jīng)過嘗試中遇到到了這種方法。

等有空我再詳細(xì)說一下這個(gè)方法吧,這一次就先到這里。

剩余的函數(shù)補(bǔ)充

首先是Genetic.py里面的八個(gè)函數(shù)


import random

#尋找最優(yōu)染色體
def findBest(pop):
    best = ['1', 0.0000001]
    for i in pop:
        if best[1] < pop[i].fitness:
            best = [i, pop[i].fitness]
    return best

#尋找最劣染色體
def findWorse(pop):
    worse = ['1', 999999]
    for i in pop:
        if worse[1] > pop[i].fitness:
            worse = [i, pop[i].fitness]
    return worse

#賦初始值
def initialize(pop, chromNodes, chromRange):
    for i in pop:
        chromList = []
        for j in range(chromNodes):
            chromList.append(random.uniform(chromRange[j][0], chromRange[j][1]+1))
        pop[i].chrom = chromList.copy()
    return pop

#計(jì)算平均適應(yīng)度
def calAveFitness(pop, N):
    sumFitness = 0
    for i in pop:
        sumFitness = sumFitness + pop[i].fitness
    aveFitness = sumFitness / N
    return aveFitness

#進(jìn)行突變
def mutChrom(pop, mut, chromNodes, bestChrom, chromRange):
    for i in pop:
        #如果隨機(jī)數(shù)小于變異概率(即可以變異)
        if mut > random.random():
            mutNode = random.randrange(0,chromNodes)
            mutRange = random.random() * (1-pop[i].fitness/bestChrom[1])**2
            pop[i].chrom[mutNode] = pop[i].chrom[mutNode] * (1+mutRange)
            #判斷變異后的范圍是否在要求范圍內(nèi)
            pop[i].chrom[mutNode] = inRange(pop[i].chrom[mutNode], chromRange[mutNode])
    return pop

#檢驗(yàn)便宜范圍是否在要求范圍內(nèi)
def inRange(mutNode, chromRange):
    if chromRange[0] < mutNode < chromRange[1]:
        return mutNode
    elif mutNode-chromRange[0] > mutNode-chromRange[1]:
        return chromRange[1]
    else:
        return chromRange[0]

#進(jìn)行交叉
def acrChrom(pop, acr, chromNodes):
    for i in pop:
        for j in pop:
            if acr > random.random():
                acrNode = random.randrange(0, chromNodes)
                #兩個(gè)染色體節(jié)點(diǎn)進(jìn)行交換
                pop[i].chrom[acrNode], pop[j].chrom[acrNode] = pop[j].chrom[acrNode], pop[i].chrom[acrNode]
    return pop

#進(jìn)行比較
def compareChrom(nowbestChrom, bestChrom):
    if bestChrom[1] > nowbestChrom[1]:
        return bestChrom
    else:
        return nowbestChrom

然后是Fitness.py的兩個(gè)函數(shù)

import math

def calFitness(pop):
    
    for i in pop:
        #計(jì)算每個(gè)染色體的適應(yīng)度
        pop[i].fitness = funcFitness(pop[i].chrom)

    return pop

def funcFitness(chrom):
    #適應(yīng)度函數(shù)
    fitness = math.sin(chrom[0])+math.cos(chrom[1])+0.1*(chrom[0]+chrom[1])

    return fitness

以上。

喜歡的話麻煩點(diǎn)個(gè)喜歡哦~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,185評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,446評(píng)論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,951評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,189評(píng)論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,718評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,602評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,800評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,045評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評(píng)論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,420評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,755評(píng)論 2 371

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