python|遗传算法基本思路与0-1决策背包问题

如何用遗传算法思路解决0-1背包问题 问题背景:共有14件物品,每件物品的重量分别为5、7、9、8、4、3、10、14、13、9、6、8、5、15,价值分别为10、8、15、9、6、5、20、10、13、10、7、12、5、18,需要用最大载重量为75的背包装载物品,要求带走的物品总价值最高。
由于遗传算法与一般线性规划和动态规划不同,本问题使用遗传算法难以一次性得出最优解。因此,可以多次使用遗传算法求解,再比较得到多次模拟后的最优解。遗传算法的思路在于模拟种群的演化:初始种群、自然选择、自由交配、随机变异。最后,经过不断筛选,种群基因库不断被优化,多次迭代后可以近似得出最适合在当下环境中生存的个体。
【python|遗传算法基本思路与0-1决策背包问题】闲话少说,我们来求解这个问题。首先结合本题要求,构建实现流程。

# 编程语言:Python import numpy as np from tqdm import tqdm # 监测程序运行进度def init(popsize, n): # 初始化种群函数 return np.random.randint(0, 2, (popsize, n))# 量化指标评估种群个体适配度 def computeFitness(population, weight, profit): total_weight = (population * weight).sum(axis=1) total_profit = (population * profit).sum(axis=1) return total_weight, total_profit# 自然选择 def select(population, weight_limit, total_weight, total_profit): pop_list = population[total_weight <= weight_limit] weight_list = total_weight[total_weight <= weight_limit] profit_list = total_profit[total_weight <= weight_limit]return pop_list, weight_list, profit_list# 轮盘赌算法扩充种数量,用筛选后的种群个体再随机扩大规模 def roulettewheel(popsize, population, profit_list): p = (profit_list / profit_list.sum()).cumsum() # 用筛选后的种群基因型再随机扩大规模得到新种群使得种群规模和之前一样大 yield_num = 0 while yield_num < popsize: select_p = np.random.uniform() yield_num += 1 # 由于一次只返回一个列表,用生成器加快速度 yield population[p > select_p][0]# 自由交配 def Pantogamy(new_population, pcross): new = [] ''' 交配后后代基因型改变(由于该模型不使用双链DNA,因此交配视为切片互换) 母方基因型: ----- | === 父方基因型: +++++ | *** 后代基因型: ----- | *** 后代基因型: +++++ | === 切片位置随机生成,父母双方随机生成 ''' while len(new) < len(new_population): if np.random.uniform() < pcross: mother_index, father_index = np.random.randint(0, len(new_population), 2) # 随即生成切片位置 threshold = np.random.randint(0, len(new_population[0])) # 不存在自交,因为本题中设定自交规则无意义 if father_index != mother_index: child1 = np.concatenate( [ new_population[father_index][:threshold], new_population[mother_index][threshold:] ] ) child2 = np.concatenate( [ new_population[mother_index][:threshold], new_population[father_index][threshold:] ] ) new.extend([child1, child2])return np.array(new)# 随机变异 def mutation(new_population, pm): for pop in range(len(new_population)): if np.random.uniform() < pm: # 达到概率,发生变异 # 随机生成变异点 point = np.random.randint(0, len(population[pop] - 1)) new_population[pop][point] = 1 - new_population[pop][point]return new_population

在主函数中调用这些函数。
if __name__ == '__main__': pm = 0.1 # 变异概率一般较小 pc = 0.9 # 发生自由交配的概率 iters = 40 # 迭代次数 popsize = 50 # 设定种群规模 n = 14 # 共有 14 件物品 weight = np.array([5, 7, 9, 8, 4, 3, 10, 14, 13, 9, 6, 8, 5, 15]) profit = np.array([10, 8, 15, 9, 6, 5, 20, 10, 13, 10, 7, 12, 5, 18]) weight_limit = 75 # 背包容量限制为 75population = init(popsize, n) # 初始化种群 for i in tqdm(range(iters - 1)): # 最后一次不需要发生变异,因此少循环一次 # 评估种群个体得分和效益 total_weight, total_profit = computeFitness(population, weight, profit) # 根据最大重量进行自然选择得到新种群 s_pop, s_w, s_p = select(population, weight_limit, total_weight, total_profit) # 轮盘赌算法扩充为和原来一样规模的种群(返回的是生成器,应将返回值实例化) new_pop = np.array(list(roulettewheel(popsize, s_pop, s_p))) # 自由交配得到子代种群 new_pop1 = Pantogamy(new_pop, pc) # 子代种群随机变异得到新种群,参与下一轮迭代 population = mutation(new_pop1, pm) # 最后一次迭代单独进行评估和自然选择过程 total_weight, total_profit = computeFitness(population, weight, profit) final_pop, final_weight, final_profit = select(population, weight_limit, total_weight, total_profit) index = final_profit.argmax() # 得到最优个体在种群中的位置print('使用遗传算法得到的(近似)最优解为: ', final_pop[index]) print('该(近似)最优解对应的物品总价值为: ', final_profit[index]) print('该(近似)最优解对应的物品总重量为: ', final_weight[index])

使用遗传算法模拟一次(代码中设定为迭代40次),得到的结果为:
python|遗传算法基本思路与0-1决策背包问题
文章图片

    推荐阅读