llwang0 2018-06-26
目的:使用遗传算法寻找方程的最大值
这里先放最后的结果:
,可以看到在经过多次迭代后,初始种群在向函数的最大值点逼近。
遗传算法是用于解决NP—hard问题的,目的是为了有效的求解出相对最优的解,用计算机上的语言来说就是以损失一定精度换取求解时间。在网上有很多描述遗传算法的,道理很简单,就是模拟八个字“物竞天择,优胜劣汰”(这里不得不佩服John Henry Holland这位大师的脑洞),学完遗传算法,蚁群算法,粒子群算法,模拟退火算法我还是想感叹一句,生活就是最美的数学。话不多说,接下来说一下我的思路。
步骤
1-确定解码方式(我采用的是二进制编码,因为更形象,也简单),取精度为0.0001,函数自变量x的范围为[0,9],那么需要的位数为17位,所以解码代码如下:
#解码 def decode(genCode): y = 0 + int(genCode,2)/(2**17-1)*9 return y 1 2 3 4
2-初始化种群(遗传算法的效果受初始化种群的影响较大,选择不好很容易陷入局部最优)
#初始化种群数(假设初始种群数目为10个),10个17位位str population = [] #总群 for i in range(20): strCode = '' for j in range(17): strCode += str(np.random.randint(0,2)) population.append(strCode) 1 2 3 4 5 6 7
3-适应函数(这里就是目标函数),求解每个个体的适应值
def fitnessfunction(population,initFunction): value = [] for i in range(len(population)): value.append(initFunction(decode(population[i]))) if value[i] < 0: value[i] = 0 return value 1 2 3 4 5 6 7 8
4-轮盘赌选择(模拟优胜劣汰),根据个体适应值大小,采用轮盘法选择优胜个体
def selectChild(population,fitnessResult): childPopulation = [] Sumfitness = [] indiviSumP = [] for i in range(len(fitnessResult)): if i == 0: Sumfitness.append(fitnessResult[i]) else: Sumfitness.append(Sumfitness[i-1] + fitnessResult[i]) for j in range(len(Sumfitness)): indiviSumP.append(Sumfitness[j]/sum(fitnessResult)) for childNum in range(len(population)): x = np.random.uniform(0,1) if x <= indiviSumP[0]: childPopulation.append(population[0]) else: for m in range(1,len(population)): if indiviSumP[m-1] <= x and x <= indiviSumP[m]: childPopulation.append(population[m]) break return childPopulation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
5-交叉选择(对选择的优胜个体进行交叉信息,模拟繁殖下一带,且用一个概率控制满足自然界中颜色体交换是普遍的(所以这里的交叉概率取固定的0.7),变异是罕见的(这里取较小0.002))
#对选择后的子代进行一定概率的染色提交换 def crossParent(new_population,pc): crossChildPopulation = [] #如果为单身则先选择出单shen if len(new_population)%2 != 0: randmSingleNum = np.random.randint(0,len(new_population)) crossChildPopulation.append(new_population[randmSingleNum]) del(new_population[randmSingleNum]) half = int(len(new_population)/2) father = new_population[:half] mother = new_population[half:] np.random.shuffle(father) np.random.shuffle(mother) for i in range(half): crossP = np.random.uniform(0,1) if pc >= crossP: breakPoint = np.random.randint(0,len(new_population[0])) son = father[i][:breakPoint] + mother[i][breakPoint:] daughter = mother[i][:breakPoint] + father[i][breakPoint:] else: son = father[i] daughter = mother[i] crossChildPopulation.append(son) crossChildPopulation.append(daughter) return crossChildPopulation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
6-变异获得子代
#变异 def variation(crossChildPopulation,pc): variationPopulation = [] for i in range(len(crossChildPopulation)): variationP = np.random.uniform(0,1) variationPos = np.random.randint(0,len(crossChildPopulation[0])) if variationP <= pc: if variationPos == 0: if crossChildPopulation[i][0] == '0': crossChildPopulation[i] = '1'+crossChildPopulation[i][1:] else: crossChildPopulation[i] = '0'+crossChildPopulation[i][1:] else: if crossChildPopulation[i][variationPos] == '0': crossChildPopulation[i] = crossChildPopulation[i][:variationPos-1] + '1' + crossChildPopulation[i][variationPos:] else: crossChildPopulation[i] = crossChildPopulation[i][:variationPos-1] + '0' + crossChildPopulation[i][variationPos:] variationPopulation.append(crossChildPopulation[i]) return variationPopulation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
然后进行多步训练即可:
for i in range(1000): fitnessValue = fitnessfunction(population,initFunction) selectChildes = selectChild(population,fitnessValue) crossChild = crossParent(selectChildes,0.7) population = variation(crossChild,0.02) 1 2 3 4 5 6
但是我们想要模拟动态过程,所以我们需要引入
from matplotlib.animation import FuncAnimation from matplotlib import animation 1 2
用于绘制我们的动图,如前面所示。这里大家可以取查官方文档看这个的具体用法,或者给我留言,我就直接贴更新图片获得动图的代码:
def updata(i): global population x1 = [0 for j in range(len(population))] y1 = [0 for j in range(len(population))] fig.set_size_inches(15,10)#控制再保存图片的时候窗口不要缩小 picpath = 'C:\Users\Amie\Desktop\png\{0}.png'.format(i) plt.savefig(picpath,pi = 600,edgecolor = 'r') fitnessValue = fitnessfunction(population,initFunction) selectChildes = selectChild(population,fitnessValue) crossChild = crossParent(selectChildes,0.7) population = variation(crossChild,0.02) for i in range(len(population)): x1[i] = decode(population[i]) y1[i] = initFunction(decode(population[i])) data = [[x,y] for x,y in zip(x1,y1)] sca1.set_offsets(data) print(list(zip(x1,y1))) return sca1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
现在我们只需要组装好所有函数即可得到,动态显示模拟遗传算法的效果图了,以下是完整代码:
from __future__ import division import math import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation from matplotlib import animation fig = plt.figure(figsize=(15,10)) ax1 = fig.add_subplot(111) ax1.set_xlim([0,10]) ax1.set_ylim([-10,15]) #定义函数 def initFunction(x): y = x + 5*math.sin(5*x)+2*math.cos(3*x) return y #形成一些点用于绘图(这里生成900个点) x = [i / 100 for i in range(900)] y = [i%900 for i in range(900)] for i in range(900): y[i] = initFunction(x[i]) ax1.plot(x,y) #初始化种群数(假设初始种群数目为10个),10个17位位str population = [] for i in range(20): strCode = '' for j in range(17): strCode += str(np.random.randint(0,2)) population.append(strCode) #解码 def decode(genCode): y = 0 + int(genCode,2)/(2**17-1)*9 return y m = [m for m in range(len(population))] n = [n for n in range(len(population))] for i in range(len(population)): n[i] = initFunction(decode(population[i])) m[i] = decode(population[i]) sca1 = ax1.scatter(m,n)#散点图命名,用于更新 # #适应度函数,其中负数置零的步骤可以调整 def fitnessfunction(population,initFunction): value = [] for i in range(len(population)): value.append(initFunction(decode(population[i]))) if value[i] < 0: value[i] = 0 return value #采用轮盘赌算法进行选择 #输入是种群的适应度函数值向量 #population 为父代,经过选择后返回子代childPopulation def selectChild(population,fitnessResult): childPopulation = [] Sumfitness = [] indiviSumP = [] for i in range(len(fitnessResult)): if i == 0: Sumfitness.append(fitnessResult[i]) else: Sumfitness.append(Sumfitness[i-1] + fitnessResult[i]) for j in range(len(Sumfitness)): indiviSumP.append(Sumfitness[j]/sum(fitnessResult)) for childNum in range(len(population)): x = np.random.uniform(0,1) if x <= indiviSumP[0]: childPopulation.append(population[0]) else: for m in range(1,len(population)): if indiviSumP[m-1] <= x and x <= indiviSumP[m]: childPopulation.append(population[m]) break return childPopulation #对选择后的子代进行一定概率的染色提交换 def crossParent(new_population,pc): crossChildPopulation = [] #如果为单身则先选择出单shen if len(new_population)%2 != 0: randmSingleNum = np.random.randint(0,len(new_population)) crossChildPopulation.append(new_population[randmSingleNum]) del(new_population[randmSingleNum]) half = int(len(new_population)/2) father = new_population[:half] mother = new_population[half:] np.random.shuffle(father) np.random.shuffle(mother) for i in range(half): crossP = np.random.uniform(0,1) if pc >= crossP: breakPoint = np.random.randint(0,len(new_population[0])) son = father[i][:breakPoint] + mother[i][breakPoint:] daughter = mother[i][:breakPoint] + father[i][breakPoint:] else: son = father[i] daughter = mother[i] crossChildPopulation.append(son) crossChildPopulation.append(daughter) return crossChildPopulation #变异 def variation(crossChildPopulation,pc): variationPopulation = [] for i in range(len(crossChildPopulation)): variationP = np.random.uniform(0,1) variationPos = np.random.randint(0,len(crossChildPopulation[0])) if variationP <= pc: if variationPos == 0: if crossChildPopulation[i][0] == '0': crossChildPopulation[i] = '1'+crossChildPopulation[i][1:] else: crossChildPopulation[i] = '0'+crossChildPopulation[i][1:] else: if crossChildPopulation[i][variationPos] == '0': crossChildPopulation[i] = crossChildPopulation[i][:variationPos-1] + '1' + crossChildPopulation[i][variationPos:] else: crossChildPopulation[i] = crossChildPopulation[i][:variationPos-1] + '0' + crossChildPopulation[i][variationPos:] variationPopulation.append(crossChildPopulation[i]) return variationPopulation def updata(i): global population x1 = [0 for j in range(len(population))] y1 = [0 for j in range(len(population))] fig.set_size_inches(15,10)#控制再保存图片的时候窗口不要缩 fitnessValue = fitnessfunction(population,initFunction) selectChildes = selectChild(population,fitnessValue) crossChild = crossParent(selectChildes,0.7) population = variation(crossChild,0.02) for i in range(len(population)): x1[i] = decode(population[i]) y1[i] = initFunction(decode(population[i])) data = [[x,y] for x,y in zip(x1,y1)] sca1.set_offsets(data) print(list(zip(x1,y1))) return sca1 ani = animation.FuncAnimation(fig = fig,func=updata,frames=np.arange(1,20),init_func = None,interval = 500,blit = False) plt.show() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
源代码链接:
链接:https://pan.baidu.com/s/1WL14a5-Q3tsqoIuJSikx4A 密码:ggu0