记录第一次写出遗传算法
问题来自遗传算法求解背包问题_用遗传算法实现背包问题-CSDN博客
首先算法思路:
一、初始一个种群
二、进行繁衍(顺带突变一下)
三、种群中单个个体的适应度(可理解为繁衍下去的概率,和目标值有关)
四、child和原种群结合成一个新种群,定义一个select函数除去哪些繁衍概率小的个体, 重复二,三进程
五、经过n次迭代(繁衍)进程 得到最终的较为优秀种群,从中寻找到适应度最大的个体(解),
当作问题的最终答案。
六、写一个for循环,看看此算法的预测准确率
一、初始一个种群
pop = np.random.randint(0, 2, size=(POP_SIZE, DNA_SIZE))
这里用二进制表示方案选择情况,010101就是指背包里面放物品2,3,6
二、进行繁衍(顺带突变一下)
突变对种群pop中的单个个体随机初始一个突变点,然后采用最简的^1逻辑判断(a^b,规则:若a==b,则判断结果为0,否则为1,所以二进制序列中可以用^1来将0,1的值颠倒)
def mutation(pop, MUTATION_RATE):
new_pop = []
for i in pop:
if np.random.rand() < MUTATION_RATE:
mutation_point = np.random.randint(0, 6)
i[mutation_point] = i[mutation_point] ^ 1
new_pop.append(i)
else:
new_pop.append(i)
return np.array(new_pop)
当然不是种群中所有个体都进行突变,用一个if语句来让突变概率为MUTATION_RATE
,同理个体中父类与子类繁衍也是一定概率
def crossover(pop, CROSSOVER_RATE):
global child
new_pop = []
for father in pop:
child = father
if np.random.rand() < CROSSOVER_RATE:
mother = pop[np.random.randint(0, POP_SIZE)]
crossover_point = np.random.randint(0, DNA_SIZE)
child[crossover_point:] = mother[crossover_point:]
new_pop.append(child)
return np.array(new_pop)
三、种群中单个个体的适应度(可理解为繁衍下去的概率,和目标值有关)
这里有个待解决的问题是:如何体现背包80公斤容量的限制
笔者采用的是将超出80kg的个体方案赋值为000000,如:随机初始化时存在个体111111(显然超出背包最大负载量)所以将其赋值为000000,这样在后面的计算适应度时,其值大小肯定是0,其nda遗传下去的概率也就是0
def update_function(pop):
new_pop = []
for i in pop:
if i.dot(np.array([10, 15, 20, 25, 30, 35])) > 80:
i = [0, 0, 0, 0, 0, 0]
new_pop.append(i)
return np.array(new_pop)
单个个体的适应度就是选择此方案时背包的价值数比上所有个体的价值数,也就是可以遗传dna的概率大小
def get_fitness(pop):
function_value = pop.dot(np.array([15, 25, 35, 45, 55, 70]))
fitness = function_value / sum(function_value)
return fitness
全部代码如下
import numpy as np
# 先设置种群的参数,
DNA_SIZE = 6 # 这里就是指的是每个个体用二进制表示的长度
POP_SIZE = 2000
CROSSOVER_RATE = 0.8 # 表示种族交配繁衍的概率
MUTATION_RATE = 0.05 # 突变概率,单个个体基因
N_GENERATIONS = 70 # 种群进行演化的代数
def update_function(pop):
new_pop = []
for i in pop:
if i.dot(np.array([10, 15, 20, 25, 30, 35])) > 80:
i = [0, 0, 0, 0, 0, 0]
new_pop.append(i)
return np.array(new_pop)
def get_fitness(pop):
function_value = pop.dot(np.array([15, 25, 35, 45, 55, 70]))
fitness = function_value / sum(function_value)
return fitness
def mutation(pop, MUTATION_RATE):
new_pop = []
for i in pop:
if np.random.rand() < MUTATION_RATE:
mutation_point = np.random.randint(0, 6) # 注意randint是不包含最大值的
i[mutation_point] = i[mutation_point] ^ 1
new_pop.append(i)
else:
new_pop.append(i)
return np.array(new_pop)
def crossover(pop, CROSSOVER_RATE):
global child
new_pop = []
for father in pop:
child = father
if np.random.rand() < CROSSOVER_RATE:
mother = pop[np.random.randint(0, POP_SIZE)]
crossover_point = np.random.randint(0, DNA_SIZE)
child[crossover_point:] = mother[crossover_point:]
new_pop.append(child)
return np.array(new_pop)
def select(pop, fitness):
idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True, p=fitness)
return pop[idx]
def print_info(pop):
fitness = get_fitness(pop)
max_fitness_idx = np.argmax(fitness)
# print("最优方案是", pop[max_fitness_idx])
# print("最优值是", pop[max_fitness_idx].dot(np.array([15, 25, 35, 45, 55, 70])))
return pop[max_fitness_idx].dot(np.array([15, 25, 35, 45, 55, 70]))
def single_process():
pop = np.random.randint(0, 2, size=(POP_SIZE, DNA_SIZE))
for _ in range(N_GENERATIONS):
pop = mutation(pop, MUTATION_RATE)
pop = crossover(pop, CROSSOVER_RATE)
fitness = get_fitness(pop)
pop = select(pop, fitness)
pop = update_function(pop)
y = print_info(pop)
return y
if __name__ == '__main__':
# pop = np.random.randint(0, 2, size=(POP_SIZE, DNA_SIZE))
# for _ in range(N_GENERATIONS):
# pop = mutation(pop, MUTATION_RATE)
# pop = crossover(pop, CROSSOVER_RATE)
# fitness = get_fitness(pop)
# pop = select(pop, fitness)
# pop = update_function(pop)
# print_info(pop)
# 写一个准确率函数
volume = []
for epoch in range(70):
y = single_process()
if y == 150:
print("预测正确")
volume.append(epoch)
else:
print("预测错误")
print(len(volume)/70)
服了,写完才发现存在一个问题,那就是没有完全模拟自然界种群的繁衍规则,虽然准确率很好。
这里求fitness大小只用来做种群的死亡选择上了,没有用到繁衍的过程(默认单个为同等概率繁衍),应当在crossover函数里面加上一个choice函数用来选择pop中的父类和母类而不是随机选择,这样种群进化的估计会更快,模型计算时间估计会减少