遗传算法
目标
用遗传算法求函数 f(x)=x*x的最大值,其中x为[0,31]间的整数
指定算法中的全局变量
DNA_SIZE = 24 #DNA长度,越长结果越精确
POP_SIZE = 200 #种群中个体的数量
CROSSOVER_RATE = 0.8 #杂交发生的概率
MUTATION_RATE = 0.005 #子代发生变异的概率
N_GENERATIONS = 50 #迭代次数
X_BOUND = [0, 31] # 个体的取值范围
初始化二进制DNA
这里问题较为简单,每个个体只有一条DNA单链
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE))
#生成种群的DNA二进制串(二维数组有POP_SIZE行,每一行是个体长度为DNA_SIZE的DNA单链)
定义目标函数
def F(x):
return x*x
DNA单链解码
def translateDNA(pop):
x = pop.dot(2**np.arange(DNA_SIZE)[::-1])/float(2**DNA_SIZE-1)*(X_BOUND[1]-X_BOUND[0])+X_BOUND[0]
return x
2**np.arange(DNA_SIZE)[::-1])/float(2**DNA_SIZE-1)
这部分将二进制串按权展开,将二进制数转化为十进制数,我们有 0 ∗ 2 9 + 1 ∗ 2 8 + 0 ∗ 2 7 + . . . + 0 ∗ 2 1 + 1 ∗ 2 0 = 373 0 ∗ 2^9 + 1 ∗ 2 ^8 + 0 ∗ 2^ 7 + . . . + 0 ∗ 2^ 1 + 1 ∗ 2 ^0 = 373 0∗29+1∗28+0∗27+...+0∗21+1∗20=373,然后将转换后的实数压缩到[ 0 , 1 ]之间的一个小数, 373 / ( 2 10 − 1 ) ≈ 0.36461388074 373 / ( 2^{10} − 1 ) ≈ 0.36461388074 373/(210−1)≈0.36461388074,通过以上这些步骤所有二进制串表示都可以转换为[ 0 , 1 ]之间的小数
压缩的小数*(X_BOUND[1]-X_BOUND[0])+X_BOUND[0]
这部分将二进制映射到个体真正的取值上,压缩的小数*(X_BOUND[1]-X_BOUND[0])就算在取值空间的增量,再加上取值空间初值,就完成了映射
适应度计算
def get_fitness(pop):
x = translateDNA(pop)
pred =F(x)
return (pred - np.min(pred)) + 1e-3
**np.min(pred)**最小适应度可能为负数,pred - np.min(pred)处理可以让所有个体的适应度都比最小适应度大,即让所有个体的适应度都为非负数,+ 1e-3是为了防止适应度为零
选择优秀个体
def select(pop, fitness): # nature selection wrt pop's fitness
idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
p=(fitness)/(fitness.sum()) )
return pop[idx]
函数使用np.random.choice函数从0到POP_SIZE-1的范围内,随机选择POP_SIZE个索引值。这些索引值代表了在当前种群pop中被选中的个体。
在选择索引值时,每个个体的概率权重是根据其适应度值与整个种群的适应度总和之间的比例计算得出的。较高适应度的个体被选中的概率更高。
最后,函数返回根据选择的索引值从pop中选择的个体集合作为下一代种群
杂交与变异
def crossover_and_mutation(pop, CROSSOVER_RATE = 0.8):
new_pop = []
for father in pop: #遍历种群中的每一个个体,将该个体作为父亲
child = father #孩子先得到父亲的全部基因(这里我把一串二进制串的那些0,1称为基因)
if np.random.rand() < CROSSOVER_RATE: #产生子代时不是必然发生交叉,而是以一定的概率发生交叉
mother = pop[np.random.randint(POP_SIZE)] #再种群中选择另一个个体,并将该个体作为母亲
cross_points = np.random.randint(low=0, high=DNA_SIZE) #随机产生交叉的点
child[cross_points:] = mother[cross_points:] #孩子得到位于交叉点后的母亲的基因
mutation(child) #每个后代有一定的机率发生变异
new_pop.append(child)
return new_pop
def mutation(child, MUTATION_RATE=0.003):
if np.random.rand() < MUTATION_RATE: #以MUTATION_RATE的概率进行变异
mutate_point = np.random.randint(0, DNA_SIZE) #随机产生一个实数,代表要变异基因的位置
child[mutate_point] = child[mutate_point]^1 #将变异点的二进制为反转
打印最好个体
def print_info(pop):
fitness = get_fitness(pop)
max_fitness_index = np.argmax(fitness)#返回适应度最大个体的索引
print("max_fitness:", fitness[max_fitness_index])
x = translateDNA(pop)
print("最优的基因型:", pop[max_fitness_index])
print("(x):", (x[max_fitness_index]))
完整代码
import numpy as np
DNA_SIZE = 24
POP_SIZE = 200
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.005
N_GENERATIONS = 50
X_BOUND = [0, 31]
def F(x):
return x*x
def translateDNA(pop):
x = pop.dot(2**np.arange(DNA_SIZE)[::-1])/float(2**DNA_SIZE-1)*(X_BOUND[1]-X_BOUND[0])+X_BOUND[0]
return x
def get_fitness(pop):
x = translateDNA(pop)
pred =F(x)
return (pred - np.min(pred)) + 1e-3
def select(pop, fitness): # nature selection wrt pop's fitness
idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
p=(fitness)/(fitness.sum()) )
return pop[idx]
def crossover_and_mutation(pop, CROSSOVER_RATE = 0.8):
new_pop = []
for father in pop: #遍历种群中的每一个个体,将该个体作为父亲
child = father #孩子先得到父亲的全部基因(这里我把一串二进制串的那些0,1称为基因)
if np.random.rand() < CROSSOVER_RATE: #产生子代时不是必然发生交叉,而是以一定的概率发生交叉
mother = pop[np.random.randint(POP_SIZE)] #再种群中选择另一个个体,并将该个体作为母亲
cross_points = np.random.randint(low=0, high=DNA_SIZE) #随机产生交叉的点
child[cross_points:] = mother[cross_points:] #孩子得到位于交叉点后的母亲的基因
mutation(child) #每个后代有一定的机率发生变异
new_pop.append(child)
return new_pop
def mutation(child, MUTATION_RATE=0.003):
if np.random.rand() < MUTATION_RATE: #以MUTATION_RATE的概率进行变异
mutate_point = np.random.randint(0, DNA_SIZE) #随机产生一个实数,代表要变异基因的位置
child[mutate_point] = child[mutate_point]^1 #将变异点的二进制为反转
def print_info(pop):
fitness = get_fitness(pop)
max_fitness_index = np.argmax(fitness)
print("max_fitness:", fitness[max_fitness_index])
x = translateDNA(pop)
print("最优的基因型:", pop[max_fitness_index])
print("(x):", (x[max_fitness_index]))
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE))
for _ in range(N_GENERATIONS):#迭代N代
x = translateDNA(pop)
pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE))
fitness = get_fitness(pop)
pop = select(pop, fitness) #选择生成新的种群
print_info(pop)
[out]>>
max_fitness: 0.0019164798162828447
最优的基因型: [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0]
(x): 30.999964892862135