程序設計的概要思想
遺傳算法是模擬自然選擇和遺傳學機理的生物進化過程的一種計算模型,主要特點爲直接對結構對象進行操作,無需求導和對函數連續性的限定,具有較好的並行性和全局尋優能力。採用概率化的尋優方法,不需要確定的規則就能自動獲取和指導優化的搜索空間,自適應地調整搜索方向。
其中,選擇、交叉和變異構成了遺傳算法的遺傳操作;編碼方案、初始羣體的設定、適應度函數的設計、遺傳操作的設計、控制參數的設定5個要素組成遺傳算法的核心。
編碼方案
遺傳算法常用的編碼方案有排列編碼、二進制編碼和實值編碼等等,考慮到編碼方案要更好地對應國際象棋棋盤上8個皇后的排列,並且不涉及到浮點數層面的逼近問題,本程序採用排列編碼。
用一元n維數組x[i]表示一個個體,i∈{1,2,3,4,5,6,7,8}。例如X[0]=1,X[7]=8,分別表示第一行第一列放置一個皇后,第八行第八列放置一個皇后。
爲了方便遺傳交叉操作,本程序採用允許編碼數組內元素重複的編碼方式。
適應度的計算
對於n皇后問題,我們可以把適應度抽象成不攻擊度。對於一個皇后,若其餘七個皇后不能俘獲它,則它的不攻擊度爲7。所以總的遺傳終止條件爲最後一代的算子中產生了適應度爲n*(n-1)/2的算子,總適應度除以2是爲了消除重複計算。在八皇后問題中n=8,即循環終止條件爲產生了適應度爲28的遺傳算子。
每個算子的初始適應度(不攻擊度)設置爲28,若此算子中的皇后行列有衝突,則適應度減一;若此算子中的皇后對角線上有衝突,即兩個皇后之間行號相減的絕對值等於列號相減的絕對值,則適應度減一。
初始種羣
本程序中的初始種羣數量爲4,對它們進行適應度排序,適應度最高的最爲父代,次高的爲第一個母代,再次的是第二個母代。父代與母代交叉產生兩個子代。
選擇算子
本程序採用競技選擇的方式,競技規模同初始種羣的規模,爲4。將這4個算子按適應度排序,適應度高的成爲下一代算子的親代。
交叉算子
本程序採用順序交叉的方式,隨機產生一個交叉點,父代算子在交叉點之前的序列加上母代交叉點之後的序列產生一個子代,父代算子在交叉點之後的序列加上母代交叉點之前的序列產生第二個子代。
這種交叉方式有助於保持親代中算子的相對位置,有助於遺傳優良的基因信息。
變異算子
本程序爲了降低交叉算子帶來的重複率,採用交換變異的方法。隨機產生兩個交換點,將子代算子的相應變異點的元素相互交換,產生變異後的算子。
終止策略
產生適應度爲28的算子,程序終止
程序的主要函數及其作用
初始化初始種羣,生成互不相同的編碼:
Initiate(a)
適應度的計算:
FitnessFunction(list)
競技選擇親代:
ChooseParents(a,b,c,d)
交叉算子:
cross(parents)
變異算子:
mutation(children)
運行結果截圖
Python源代碼
import random
import matplotlib.pyplot
#初始化生成種羣,種羣容量爲4,爲了提高迭代效率,使初始羣體的8個編碼各不相同
def Initiate(a):
while len(a)<8:
b=random.randint(1,8)
if not(b in a):
a.append(b)
return a
#適應度的計算,即不攻擊度。若一個皇后不攻擊其餘皇后,則其不攻擊度爲8-1=7。終止條件爲8個皇后不攻擊度之和爲8*7/2(考慮重複計算)
def FitnessFunction(list):
FitnessIndex=[28,28,28,28]
for i in range(0, 4):
for j in range(0, 7):
for k in range(j+1, 8):
if list[i][j]==list[i][k]:
FitnessIndex[i] -= 1
if list[i][j]-list[i][k] == j-k or list[i][j]-list[i][k] == k-j:
FitnessIndex[i] -= 1
fitness = {'a': FitnessIndex[0], 'b': FitnessIndex[1], 'c': FitnessIndex[2], 'd': FitnessIndex[3]}
return fitness
#選擇親代算子,這裏使用競爭選擇
def ChooseParents(a,b,c,d):
stack=[]
stack.append(a)
stack.append(b)
stack.append(c)
stack.append(d)
fitness = FitnessFunction(stack)
sort=sorted(fitness.items(), key=lambda e : e[1], reverse=True)
if sort[0][0] == 'a':
firstfather = a
elif sort[0][0] == 'b':
firstfather = b
elif sort[0][0] == 'c':
firstfather = c
else:
firstfather = d
if sort[1][0] == 'a':
firstmother = a
elif sort[1][0] == 'b':
firstmother = b
elif sort[1][0] == 'c':
firstmother = c
else:
firstmother = d
parents=[]
parents.append(firstfather)
parents.append(firstmother)
parents.append(firstfather)
if sort[0][0] == 'a':
secmother = a
elif sort[0][0] == 'b':
secmother = b
elif sort[0][0] == 'c':
secmother = c
else:
secmother = d
parents.append(secmother)
return parents
#交叉算子,本程序採用順序交叉
def cross(parents):
children = []
crosspoint=random.randint(1,7)
firstfather,firstmother,secfather,secmother=parents[0],parents[1],parents[2],parents[3]
ch1 = firstfather[0:crosspoint]
ch1.extend(firstmother[crosspoint:])
ch2 = firstmother[0:crosspoint]
ch2.extend(firstfather[crosspoint:])
ch3 = secfather[0:crosspoint]
ch3.extend(secmother[crosspoint:])
ch4 = secmother[0:crosspoint]
ch4.extend(secfather[crosspoint:])
children.append(ch1)
children.append(ch2)
children.append(ch3)
children.append(ch4)
return children
#變異,這裏採用交換變異
def mutation(children):
muchildren=[]
mtpoint1=random.randint(0,7)
mtpoint2=random.randint(0,7)
ch1, ch2, ch3, ch4 = children[0], children[1], children[2], children[3]
ch1[mtpoint1], ch1[mtpoint2] = ch1[mtpoint2], ch1[mtpoint1]
mtpoint1=random.randint(0,7)
mtpoint2=random.randint(0,7)
ch2[mtpoint1], ch2[mtpoint2] = ch2[mtpoint2], ch2[mtpoint1]
mtpoint1=random.randint(0,7)
mtpoint2=random.randint(0,7)
ch3[mtpoint1], ch3[mtpoint2] = ch3[mtpoint2], ch3[mtpoint1]
mtpoint1=random.randint(0,7)
mtpoint2=random.randint(0,7)
ch4[mtpoint1], ch4[mtpoint2] = ch4[mtpoint2], ch4[mtpoint1]
muchildren.append(ch1)
muchildren.append(ch2)
muchildren.append(ch3)
muchildren.append(ch4)
return muchildren
a,b,c,d=[],[],[],[]
Initiate(a)
Initiate(b)
Initiate(c)
Initiate(d)
parents=ChooseParents(a,b,c,d)
#爲防止程序陷入死循環,設置最大繁衍次數
maxGenerations=100000
generations=0
for i in range(maxGenerations):
cparents=cross(parents)
mparents=mutation(cparents)
generations+=1
fitness = FitnessFunction(mparents)
if fitness['a'] == 28 or fitness['b'] == 28 or fitness['c'] == 28 or fitness['d'] == 28:
answer = []
if fitness['a'] == 28:
answer.append(mparents[0])
if fitness['b'] == 28:
answer.append(mparents[1])
if fitness['c'] == 28:
answer.append(mparents[2])
if fitness['d'] == 28:
answer.append(mparents[3])
print("This answer is {0}".format(answer))
print("Find if after {0} times".format(generations))
#可行解的可視化
for i in answer:
y=[i.index(1)+0.5, i.index(2)+0.5, i.index(3)+0.5, i.index(4)+0.5, i.index(5)+0.5, i.index(6)+0.5, i.index(7)+0.5, i.index(8)+0.5]
x=[0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5]
matplotlib.pyplot.title("Using Genetic Algorithm to Solve 8-Queens' Problem")
matplotlib.pyplot.axis([0,8,0,8])
matplotlib.pyplot.grid()
matplotlib.pyplot.plot(x, y, '*')
matplotlib.pyplot.show()
break
else:
parents=mparents
if maxGenerations==generations and answer==[]:
print("Cannot find the answer")