问题:求
在
上的最大值。
解题步骤:
1、随机生成种群
,每个种群中含有
条染色体。每个染色体上含有10个基因(以二进制表示自变量,故每条染色体上表示的数字范围是
)。
2、计算这些染色体的适应度,适应度大的在下一步被选到的概率大。
3、依概率从100条染色体中有放回地抽取100条染色体(有重复染色体),令这新的100条染色体组成新的种群
。
4、将
复制为
来表示另一条父染色体用来与
中的染色体进行交叉配对。
5、从
中选出第一条染色体进行交叉检验。
6、假设交叉的概率为
,则
中的每条染色体都有
的概率需要交叉,假设第一条染色体是要进行交叉的。(若未被选中则继续第二条染色体的交叉检验,直到遍历完
中所有染色体)
7、随机在
中选择一条染色体与
中第一条染色体进行交叉,假设选中
中第三条染色体。
8、选择
中第一条染色体上需要改变的基因是哪些,假设为前三个基因。
9、将
中第三条染色体上的前三个基因取出放到
中第一条染色体上的前三个位置。交叉完毕,得到一条子染色体
。
10、对
进行编变异操作,假设变异概率为
。
11、依次对
上所有基因进行变异检验,如果某基因需要变异,则将此处位置的数字取反(
)。
12、用经过了交叉变异的
代替
中的父染色体(即第一条染色体)。
13、重复以上操作,遍历完
中所有染色体后,第一次种群更新完成。
以下为
代码:
import numpy as np
DNA_SIZE = 10 # DNA length
POP_SIZE = 100 # population size
CROSS_RATE = 0.8 # mating probability (DNA crossover)
MUTATION_RATE = 0.003 # mutation probability
N_GENERATIONS = 200
X_BOUND = [0, 5] # x upper and lower bounds
def F(x):
return np.sin(10*x)*x + np.cos(2*x)*x # to find the maximum of this function
def get_fitness(pred):
return pred - np.min(pred) + 1e-3 # 防止出现非正值,因为 select 时概率非负。
def translateDNA(pop):
# np.arange(DNA_SIZE)[::-1] --> array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
# 2 ** np.arange(DNA_SIZE)[::-1] --> array([512, 256, 128, 64, 32, 16, 8, 4, 2, 1], dtype=int32)
# pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) --> 将二进制转换为十进制
# (2 ** DNA_SIZE - 1) --> 1023
# pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / (2 ** DNA_SIZE - 1) --> 计算染色体值占最大值(1023)的比例
# pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / (2 ** DNA_SIZE - 1) * X_BOUND[1] --> 将范围从[0, 1023]缩放到[0, 5]
return pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / (2 ** DNA_SIZE - 1) * X_BOUND[1]
def select(pop, fitness):
# 在 np.arange(POP_SIZE) 中依概率 p 选出 size 个染色体代号
# replace=True 表示选出的染色体可以有重复的
idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True, p=fitness / fitness.sum())
return pop[idx]
def crossover(parent, pop):
if np.random.rand() < CROSS_RATE:
i_ = np.random.randint(0, POP_SIZE, size=1) # 选定 pop 中需要交叉的染色体,如 i_ = 1,表示第二条染色体
# 选择需要改变的基因,如 cross_points = [False True True False False True False True True False]
# 表示第2,3,6,8,9个位置上的基因会改变
cross_points = np.random.randint(0, 2, size=DNA_SIZE).astype(np.bool)
# pop[i_, cross_points] 会得到 pop 种群中 i_ 这条染色体上的 cross_points 中 True 位置上的基因
# parent[cross_points] = pop[i_, cross_points] 将刚刚得到的基因放到 parent 这条染色体的 cross_points 中 True 的位置上
parent[cross_points] = pop[i_, cross_points]
return parent
def mutate(child):
for point in range(DNA_SIZE):
if np.random.rand() < MUTATION_RATE:
child[point] = 1 if child[point] == 0 else 0
return child
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE)) # initialize the pop DNA
print(pop.shape)
for _ in range(N_GENERATIONS):
F_values = F(translateDNA(pop)) # compute function value by extracting DNA
# GA part (evolution)
fitness = get_fitness(F_values)
print("Most fitted DNA: ", pop[np.argmax(fitness), :], "适应度为:", round(fitness[np.argmax(fitness)], 3))
pop = select(pop, fitness)
pop_copy = pop.copy()
for parent in pop:
child = crossover(parent, pop_copy)
child = mutate(child)
parent = child # 用经过交叉变异的下一代(新的染色体)来代替上一代(pop 种群中选出的)
print('最大值为:', F(translateDNA(pop[np.argmax(fitness), :])))