今天整理以前寫的代碼,發如今作數模期間寫的用python實現的遺傳算法,感受仍是挺有意思的,就拿出來分享一下。
首先遺傳算法是一種優化算法,經過模擬基因的優勝劣汰,進行計算(具體的算法思路什麼的就不贅述了)。大體過程分爲初始化編碼、個體評價、選擇,交叉,變異。
遺傳算法介紹
遺傳算法是經過模擬大天然中生物進化的歷程,來解決問題的。大天然中一個種羣經歷過若干代的天然選擇後,剩下的種羣一定是適應環境的。把一個問題全部的解看作一個種羣,經歷過若干次的天然選擇之後,剩下的解中是有問題的最優解的。固然,只能說有最優解的機率很大。這裏,咱們用遺傳算法求一個函數的最大值。
f(x) = 10 * sin( 5x ) 7 * cos( 4x ), 0 <= x <= 10
一、將自變量x進行編碼
取基因片斷的長度爲10, 則10位二進制位能夠表示的範圍是0到1023。基因與自變量轉變的公式是x = b2d(individual) * 10 / 1023。構造初始的種羣pop。每一個個體的基因初始值是[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
二、計算目標函數值
根據自變量與基因的轉化關係式,求出每一個個體的基因對應的自變量,而後將自變量代入函數f(x),求出每一個個體的目標函數值。
三、適應度函數
適應度函數是用來評估個體適應環境的能力,是進行天然選擇的依據。本題的適應度函數直接將目標函數值中的負值變成0. 由於咱們求的是最大值,因此要使目標函數值是負數的個體不適應環境,使其繁殖後代的能力爲0.適應度函數的做用將在天然選擇中體現。
四、天然選擇
天然選擇的思想再也不贅述,操做使用輪盤賭算法。其具體步驟:
假設種羣中共5個個體,適應度函數計算出來的個體適應性列表是fitvalue = [1 ,3, 0, 2, 4] ,totalvalue = 10 , 若是將fitvalue畫到圓盤上,值的大小表示在圓盤上的面積。在轉動輪盤的過程當中,單個模塊的面積越大則被選中的機率越大。選擇的方法是將fitvalue轉化爲[1 , 4 ,4 , 6 ,10], fitvalue / totalvalue = [0.1 , 0.4 , 0.4 , 0.6 , 1.0] . 而後產生5個0-1之間的隨機數,將隨機數從小到大排序,假如是[0.05 , 0.2 , 0.7 , 0.8 ,0.9],則將0號個體、1號個體、4號個體、4號個體、4號個體拷貝到新種羣中。天然選擇的結果使種羣更符合條件了。
五、繁殖
假設個體a、b的基因是
a = [1, 0, 0, 0, 0, 1, 1, 1, 0, 0]
b = [0, 0, 0, 1, 1, 0, 1, 1, 1, 1]
這兩個個體發生基因交換的機率pc = 0.6.若是要發生基因交換,則產生一個隨機數point表示基因交換的位置,假設point = 4,則:
a = [1, 0, 0, 0, 0, 1, 1, 1, 0, 0]
b = [0, 0, 0, 1, 1, 0, 1, 1, 1, 1]
交換後爲:
a = [1, 0, 0, 0, 1, 0, 1, 1, 1, 1]
b = [0, 0, 0, 1, 0, 1, 1, 1, 0, 0]
六、突變
遍歷每個個體,基因的每一位發生突變(0變爲1,1變爲0)的機率爲0.001.突變能夠增長解空間
以目標式子 y = 10 * sin(5x) 7 * cos(4x)爲例,計算其最大值
首先是初始化,包括具體要計算的式子、種羣數量、染色體長度、交配機率、變異機率等。而且要對基因序列進行初始化
pop_size = 500 # 種羣數量
max_value = 10 # 基因中容許出現的最大值
chrom_length = 10 # 染色體長度
pc = 0.6 # 交配機率
pm = 0.01 # 變異機率
results = [[]] # 存儲每一代的最優解,N個二元組
fit_value = [] # 個體適應度
fit_mean = [] # 平均適應度
pop = geneEncoding(pop_size, chrom_length)
其中genEncodeing是自定義的一個簡單隨機生成序列的函數,具體實現以下
def geneEncoding(pop_size, chrom_length):
pop = [[]]
for i in range(pop_size):
temp = []
for j in range(chrom_length):
temp.append(random.randint(0, 1))
pop.append(temp)
return pop[1:]
編碼完成以後就是要進行個體評價,個體評價主要是計算各個編碼出來的list的值以及對應帶入目標式子的值。其實編碼出來的就是一堆2進制list。這些2進制list每一個都表明了一個數。其值的計算方式爲轉換爲10進制,而後除以2的序列長度次方減一,也就是全一list的十進制減一。根據這個規則就能計算出全部list的值和帶入要計算式子中的值,代碼以下
# 0.0 coding:utf-8 0.0
# 解碼並計算值
import math
def decodechrom(pop, chrom_length):
temp = []
for i in range(len(pop)):
t = 0
for j in range(chrom_length):
t = pop[i][j] * (math.pow(2, j))
temp.append(t)
return temp
def calobjValue(pop, chrom_length, max_value):
temp1 = []
obj_value = []
temp1 = decodechrom(pop, chrom_length)
for i in range(len(temp1)):
x = temp1[i] * max_value / (math.pow(2, chrom_length) - 1)
obj_value.append(10 * math.sin(5 * x) 7 * math.cos(4 * x))
return obj_value
有了具體的值和對應的基因序列,而後進行一次淘汰,目的是淘汰掉一些不可能的壞值。這裏因爲是計算最大值,因而就淘汰負值就行了
# 0.0 coding:utf-8 0.0
# 淘汰(去除負值)
def calfitValue(obj_value):
fit_value = []
c_min = 0
for i in range(len(obj_value)):
if(obj_value[i] c_min > 0):
temp = c_min obj_value[i]
else:
temp = 0.0
fit_value.append(temp)
return fit_value
而後就是進行選擇,這是整個遺傳算法最核心的部分。選擇實際上模擬生物遺傳進化的優勝劣汰,讓優秀的個體儘量存活,讓差的個體儘量的淘汰。個體的好壞是取決於個體適應度。個體適應度越高,越容易被留下,個體適應度越低越容易被淘汰。具體的代碼以下
# 0.0 coding:utf-8 0.0
# 選擇
import random
def sum(fit_value):
total = 0
for i in range(len(fit_value)):
total = fit_value[i]
return total
def cumsum(fit_value):
for i in range(len(fit_value)-2, -1, -1):
t = 0
j = 0
while(j <= i):
t = fit_value[j]
j = 1
fit_value[i] = t
fit_value[len(fit_value)-1] = 1
def selection(pop, fit_value):
newfit_value = []
# 適應度總和
total_fit = sum(fit_value)
for i in range(len(fit_value)):
newfit_value.append(fit_value[i] / total_fit)
# 計算累計機率
cumsum(newfit_value)
ms = []
pop_len = len(pop)
for i in range(pop_len):
ms.append(random.random())
ms.sort()
fitin = 0
newin = 0
newpop = pop
# 轉輪盤選擇法
while newin < pop_len:
if(ms[newin] < newfit_value[fitin]):
newpop[newin] = pop[fitin]
newin = newin 1
else:
fitin = fitin 1
pop = newpop
以上代碼主要進行了3個操做,首先是計算個體適應度總和,而後在計算各自的累積適應度。這兩步都好理解,主要是第三步,轉輪盤選擇法。這一步首先是生成基因總數個0-1的小數,而後分別和各個基因的累積個體適應度進行比較。若是累積個體適應度大於隨機數則進行保留,不然就淘汰。這一塊的核心思想在於:一個基因的個體適應度越高,他所佔據的累計適應度空隙就越大,也就是說他越容易被保留下來。
選擇完後就是進行交配和變異,這個兩個步驟很好理解。就是對基因序列進行改變,只不過改變的方式不同
交配:
# 0.0 coding:utf-8 0.0
# 交配
import random
def crossover(pop, pc):
pop_len = len(pop)
for i in range(pop_len - 1):
if(random.random() < pc):
cpoint = random.randint(0,len(pop[0]))
temp1 = []
temp2 = []
temp1.extend(pop[i][0:cpoint])
temp1.extend(pop[i 1][cpoint:len(pop[i])])
temp2.extend(pop[i 1][0:cpoint])
temp2.extend(pop[i][cpoint:len(pop[i])])
pop[i] = temp1
pop[i 1] = temp2
變異:
# 0.0 coding:utf-8 0.0
# 基因突變
import random
def mutation(pop, pm):
px = len(pop)
py = len(pop[0])
for i in range(px):
if(random.random() < pm):
mpoint = random.randint(0, py-1)
if(pop[i][mpoint] == 1):
pop[i][mpoint] = 0
else:
pop[i][mpoint] = 1
整個遺傳算法的實現完成了,總的調用入口代碼以下
# 0.0 coding:utf-8 0.0
import matplotlib.pyplot as plt
import math
from calobjValue import calobjValue
from calfitValue import calfitValue
from selection import selection
from crossover import crossover
from mutation import mutation
from best import best
from geneEncoding import geneEncoding
print 'y = 10 * math.sin(5 * x) 7 * math.cos(4 * x)'
# 計算2進制序列表明的數值
def b2d(b, max_value, chrom_length):
t = 0
for j in range(len(b)):
t = b[j] * (math.pow(2, j))
t = t * max_value / (math.pow(2, chrom_length) - 1)
return t
pop_size = 500 # 種羣數量
max_value = 10 # 基因中容許出現的最大值
chrom_length = 10 # 染色體長度
pc = 0.6 # 交配機率
pm = 0.01 # 變異機率
results = [[]] # 存儲每一代的最優解,N個二元組
fit_value = [] # 個體適應度
fit_mean = [] # 平均適應度
# pop = [[0, 1, 0, 1, 0, 1, 0, 1, 0, 1] for i in range(pop_size)]
pop = geneEncoding(pop_size, chrom_length)
for i in range(pop_size):
obj_value = calobjValue(pop, chrom_length, max_value) # 個體評價
fit_value = calfitValue(obj_value) # 淘汰
best_individual, best_fit = best(pop, fit_value) # 第一個存儲最優的解, 第二個存儲最優基因
results.append([best_fit, b2d(best_individual, max_value, chrom_length)])
selection(pop, fit_value) # 新種羣複製
crossover(pop, pc) # 交配
mutation(pop, pm) # 變異
results = results[1:]
results.sort()
X = []
Y = []
for i in range(500):
X.append(i)
t = results[i][0]
Y.append(t)
plt.plot(X, Y)
plt.show()
最後調用了一下matplotlib包,把500代最優解的變化趨勢表現出來。
以上就是本文的所有內容,但願對你們的學習有所幫助python