遺傳演算法之Python實現

寫在前面

之前的文章中已經講過了遺傳演算法的基本流程(沒看過的朋友可以點我的CSDN博客),並且用MATLAB實現過一遍了。這一篇文章主要面對的人群是看過了我之前的文章,因此我就不再贅述遺傳演算法是什麼以及基本的內容了,假設大家已經知道我是怎麼寫遺傳演算法的了。

Python的遺傳演算法主函數

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

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

所以我們開始設置基礎參數。其中種群的表達方式我用的是字典,也就是用一個字典來保存種群內的所有個體,這個也是我想出來的創建多個對象的方法。

將字典的索引為個體的標號,如:chrom1, chrom2等。字典索引的值就是一個對象。這個對象擁有兩個屬性,就是染色體與適應度。

其實在這一方便來說,我覺得在思路上是優於利用MATLAB的矩陣式編程的。因為這樣可以很直觀的將個體與個體的屬性這一種思想給表達出來,相比一堆矩陣來說,在邏輯上比較容易接受。

#基礎參數N = 200 #種群內個體數目mut = 0.2 #突變概率acr = 0.2 #交叉概率pop = {} #存儲染色體的字典for i in range(N): pop[chrom+str(i)] = Chrom()chromNodes = 2 #染色體節點數(變數個數)iterNum = 10000 #迭代次數chromRange = [[0, 10], [0, 10]] #染色體範圍aveFitnessList = [] #平均適應度bestFitnessList = [] #最優適應度

之後就是初始染色體了,其中就牽扯到了各種用來初始化種群、計算適應度、找最優等函數,我在這裡分出了兩個文件,分別為Genetic.py和Fitness.py。

Genetic.py裡面有八個函數,主要包含了作用於種群或者染色體操作的函數,分別為:

  • - findBest函數,用於尋找種群中的最優染色體;
  • - findworse函數,用於尋找種群中的最劣染色體;
  • - initialize函數,用於初始化種群;
  • - calAveFitness函數,用於計算種群的平均適應度;
  • - mutChrom函數,用於對染色體進行變異;
  • - inRange函數,用於判斷染色體節點值是否越界;
  • - acrChrom函數,用於對染色體進行交叉;

- compareChrom函數,用於比較兩個染色體孰優孰劣。

Fitness.py裡面有兩個函數,主要包含了對適應度操作的函數,分別為:

  • - calFitness函數,用來迭代每一個個體,並計算適應度(利用funcFitness函數計算);
  • - funcFitness函數,計算單個個體的適應度。

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

#初始染色體pop = Genetic.initialize(pop, chromNodes, chromRange)pop = Fitness.calFitness(pop) #計算適應度bestChrom = Genetic.findBest(pop) #尋找最優染色體bestFitnessList.append(bestChrom[1]) #將當前最優適應度壓入列表中aveFitnessList.append(Genetic.calAveFitness(pop, N)) #計算並存儲平均適應度

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

#開始迭代for t in range(iterNum): #染色體突變 pop = Genetic.mutChrom(pop, mut, chromNodes, bestChrom, chromRange) #染色體交換 pop = Genetic.acrChrom(pop, acr, chromNodes) #尋找最優 nowBestChrom = Genetic.findBest(pop) #比較前一個時間的最優和現在的最優 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 #存儲最優與平均 bestFitnessList.append(bestChrom[1]) aveFitnessList.append(Genetic.calAveFitness(pop, N))

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

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

最後再在最前面加上各種庫和文件就可以運行了。

import Geneticimport Fitnessimport matplotlib.pyplot as pltimport numpy as np

感悟

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

深刻感悟到了面向對象編程的優點,在編程邏輯的處理上真是一種享受,只需要思考對象的屬性即可,省去了許多複雜的思考。

另一個感悟就是創建多個對象時,利用字典的方法來創建對象。當初我也是困惑怎麼建立一個類似於C++中的對象數組,上網查找了各種方法,結果都避而不談(當然,也可能是我搜索能力太差沒找到),所以經過嘗試中遇到到了這種方法。

有空我再詳細說一下這個方法吧,這一次就先到這裡。

剩餘的函數補充

首先是Genetic.py裡面的八個函數

import random#尋找最優染色體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#計算平均適應度def calAveFitness(pop, N): sumFitness = 0 for i in pop: sumFitness = sumFitness + pop[i].fitness aveFitness = sumFitness / N return aveFitness#進行突變def mutChrom(pop, mut, chromNodes, bestChrom, chromRange): for i in pop: #如果隨機數小於變異概率(即可以變異) 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) #判斷變異後的範圍是否在要求範圍內 pop[i].chrom[mutNode] = inRange(pop[i].chrom[mutNode], chromRange[mutNode]) return pop#檢驗便宜範圍是否在要求範圍內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]#進行交叉def acrChrom(pop, acr, chromNodes): for i in pop: for j in pop: if acr > random.random(): acrNode = random.randrange(0, chromNodes) #兩個染色體節點進行交換 pop[i].chrom[acrNode], pop[j].chrom[acrNode] = pop[j].chrom[acrNode], pop[i].chrom[acrNode] return pop#進行比較def compareChrom(nowbestChrom, bestChrom): if bestChrom[1] > nowbestChrom[1]: return bestChrom else: return nowbestChrom

然後是Fitness.py的兩個函數

import mathdef calFitness(pop): for i in pop: #計算每個染色體的適應度 pop[i].fitness = funcFitness(pop[i].chrom) return popdef funcFitness(chrom): #適應度函數 fitness = math.sin(chrom[0])+math.cos(chrom[1])+0.1*(chrom[0]+chrom[1]) return fitness

以上。


老哥們可以去我的CSDN博客看更多的文章。

Hanpu_Liang的博客 - CSDN博客


推薦閱讀:

MySQL與Python的交互
python單個腳本的日誌記錄功能簡單使用
百年百圖の中國(1900-1999):另類python爬蟲和PIL拼圖

TAG:Python | 數學模型 | 遺傳演算法 |