进化策略
问题:求
在
上的最大值。
解题步骤:
1、随机生成种群
,假设种群中含
条染色体以及每条染色体的变异概率。因为函数自变量只有一个,故每条染色体上只有一个基因。
2、从
中随机选出两条染色体(以及它们的变异概率)来产生一条子染色体。设这两条染色体为
和
。
3、设由这两条父染色体产生的子染色体及其变异概率分别为
和
,将
或
以
的概率复制给
和
。
4、子染色体(及其变异概率)发生变异。
5、设一代中产生50条子染色体,则将以上过程重复进行50次。
6、将产生的50条子染色体与100条原始父染色体放在一起,计算它们的适应度(即
值)。
7、按适应度对150条染色体进行排序,选取适应度最大的前100条染色体,将它们作为新的父代染色体重复以上过程,直到所有染色体都被最优染色体取代或达到迭代次数。
代码如下:
import numpy as np
DNA_SIZE = 1 # DNA (real number)
DNA_BOUND = [0, 5] # solution upper and lower bounds
N_GENERATIONS = 200
POP_SIZE = 100 # population size
N_KID = 50 # n kids per generation
def F(x): return np.sin(10*x)*x + np.cos(2*x)*x # to find the maximum of this function
# find non-zero fitness for selection
def get_fitness(pred): return pred.flatten()
def make_kid(pop, n_kid):
# generate empty kid holder
kids = {'DNA': np.empty((n_kid, DNA_SIZE))}
kids['mut_strength'] = np.empty_like(kids['DNA'])
for kv, ks in zip(kids['DNA'], kids['mut_strength']):
# crossover (roughly half p1 and half p2)
p1, p2 = np.random.choice(np.arange(POP_SIZE), size=2, replace=False)
cp = np.random.randint(0, 2, DNA_SIZE, dtype=np.bool) # crossover points
kv[cp] = pop['DNA'][p1, cp] # 若 cp 为 True,则将 pop['DNA'][p1] 的值赋给 kv,否则不变。
kv[~cp] = pop['DNA'][p2, ~cp] # 若 cp 为 False,则将 pop['DNA'][p2] 的值赋给 kv,否则不变。
ks[cp] = pop['mut_strength'][p1, cp]
ks[~cp] = pop['mut_strength'][p2, ~cp]
# mutate (change DNA based on normal distribution)
ks[:] = np.maximum(ks + (np.random.rand(*ks.shape)-0.5), 0.) # must > 0
kv += ks * np.random.randn(*kv.shape)
# clip这个函数将将数组中的元素限制在a_min, a_max之间,大于a_max的就使它等于 a_max,小于a_min的就使它等于a_min。
kv[:] = np.clip(kv, *DNA_BOUND)
return kids
def kill_bad(pop, kids):
# put pop and kids together
for key in ['DNA', 'mut_strength']:
pop[key] = np.vstack((pop[key], kids[key])) # 将新产生的染色体(包括变异程度)与父代染色体放到一起作比较
fitness = get_fitness(F(pop['DNA'])) # calculate global fitness
idx = np.arange(pop['DNA'].shape[0])
# argsort函数返回的是数组值从小到大的索引值
# [-POP_SIZE:] 指从后往前取 POP_SIZE 个,保持种群中染色体数量不变
good_idx = idx[fitness.argsort()][-POP_SIZE:]
for key in ['DNA', 'mut_strength']:
pop[key] = pop[key][good_idx]
return pop
pop = dict(DNA=5 * np.random.rand(1, DNA_SIZE).repeat(POP_SIZE, axis=0), # initialize the pop DNA values
mut_strength=np.random.rand(POP_SIZE, DNA_SIZE)) # initialize the pop mutation strength values
for _ in range(N_GENERATIONS):
# ES part
kids = make_kid(pop, N_KID)
pop = kill_bad(pop, kids) # keep some good parent for elitism
print(F(pop['DNA'][0]) # 最优解