版权声明:本文为威哥哥带你写代码原创文章,有错请评论,转载请注明,谢谢
遗传算法
遗传算法是一种随机自适应的随机搜索算法,从一定程度上反应了达尔文的进化理论"自然选择"和“优胜劣汰”,算法流程并不复杂,大致分为6个部分,分别是染色体编码,种群初始化,适应度评价,选择种群,交配,变异。其中染色体编码相对来说比较复杂,需要将十进制转化为相应的二进制,其中针对不同的精确度需要分析不同的二进制位数,本文主要针对维度为1的函数进行优化,针对高维函数的代码将在下一篇文章中展示。
染色体编码和种群初始化
博主这里的精确度取的0.001,定义域【0,5】,所以需要10个二进制位,种群数量取的100,所以需要生成100个长度为10的二进制编码。
初始化:
def initialize():
pop=np.random.randint(2,size=(100,10))
return pop
这里我们可以看看种群(部分)
array([[0, 1, 1, 0, 1, 0, 1, 1, 0, 1],
[1, 0, 1, 1, 0, 1, 0, 0, 0, 1],
[0, 1, 1, 1, 1, 1, 1, 0, 1, 1],
[0, 0, 0, 1, 0, 0, 1, 0, 1, 0],
[0, 0, 0, 0, 1, 0, 1, 1, 1, 1],
[1, 0, 1, 0, 0, 1, 0, 1, 0, 1],
[0, 1, 0, 0, 0, 1, 1, 1, 0, 1],
[1, 0, 0, 1, 1, 0, 1, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 1, 0, 1, 1],
[1, 0, 1, 0, 1, 1, 0, 1, 1, 0],
[1, 1, 1, 0, 0, 1, 1, 0, 1, 0],
[0, 1, 1, 0, 0, 0, 1, 0, 1, 0],
[1, 1, 1, 1, 0, 1, 0, 1, 0, 1],
[1, 1, 1, 0, 0, 1, 1, 1, 1, 0],
[1, 1, 0, 0, 0, 1, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 1, 1],
........
如果相应的个体不再定义域内部怎么办?这里就需要用等分的思想稍微转化一下
pop.dot(2**np.arange(10)[::-1])/float(2**10-1)*5
其中5是定义域的长度,等分成2^10-1分,然后乘对应的大小就完成了转换。
适应度和选择
适应度的评估函数用于评估每个染色体的适应值,用于进行优胜劣汰的自然选择,每个染色体的适应度为F(xi)/sum(F(xi));对应的适应度越大则在自然选择过程中的概率也越大。
def fitness(pop):
for i in range(100):
pop[i]=F(pop[i])
if pop[i]<0:
pop[i]=0
return pop
def selection(pop,fit,sum_fitness):
i = np.random.choice(np.arange(100), size=100, replace=True, p=fit/sum_fitness)
其中np.random.choice()的用法在这里不做解释,请自行百度。
关于if pop[i]<0: pop[i]=0
这里是因为后面要算适应度,适应度也即个体被选择的概率,不应该小于0,所以将小于0的元素全部换成0代替。
选择交配
首先这里需要设置一个种群交配概率,博主设置的为0.8,被选择的种群还需要选择交换的DNA片段,这里关于DNA的交换片段可以根据1/2原则进行设置,这里用的是生成0 1随机数然后转化成bool型实现的。
def crossover(parent,pop):
if np.random.rand()<0.8:
i= np.random.randint(0, 100, size=1) #选择母体进行交配
point=np.random.randint(0,2,size=10).astype(np.bool)
parent[point]=pop[i,point]
return parent
选择变异
和交配一样,首先设置一个变异的概率,然后针对不同的个体进行每一个染色体中DNA片段的等概率变异,这里的变异概率设置为0.03
def mutation(m):
for i in range(10):
if np.random.rand()<0.003:
if m[i]==1:
[i]=0
else:
[i]=1
return m
代码主体
代码总体部分非常简单,设置一个循环即可搞定。流程图为
pop=selection(pop,fit,sum(fit))
pop_copy = pop.copy()
for parent in pop:
m = crossover(parent,pop_copy)
m = mutation(m)
目标函数
def F(x):
y=5*x+5*np.sin(5*x)+2*np.cos(3*x)
return y
运行效果
可以看见是逐步向最大值移动的,虽然效率有待提高,不是最终可以针对多为函数的代码版本,这里就不放完整代码了,需要的可以评论留邮箱。