遗传算法是模拟自然选择和遗传学机理的生物进化过程的一种计算模型,主要特点为直接对结构对象进行操作,无需求导和对函数连续性的限定,具有较好的并行性和全局寻优能力。采用概率化的寻优方法,不需要确定的规则就能自动获取和指导优化的搜索空间,自适应地调整搜索方向。
其中,选择、交叉和变异构成了遗传算法的遗传操作;编码方案、初始群体的设定、适应度函数的设计、遗传操作的设计、控制参数的设定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)
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")