遗传算法求解01背包问题记录

urlyy

感觉就是在大方向正确(优胜劣汰)的情况下进行随机的变化,试图找到最优解

  graph TD;
     START(输入数据)-->B[编码];
     B[编码]-->C[种群old];
     C-->D[计算各个体适应值];
     D-->E[随机选择两个个体进行交叉运算];
     E-->F[随机选择个体进行变异];
     F-->G[生成种群new];
     G-->H[使用new更新old];
     H-->I{满足要求或到达迭代次数}
     I-->|Y|J[解码种群old];
     I-->|N|C[种群old];
     J-->END(目标解);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
from functools import reduce
import matplotlib.pyplot as plt
import numpy as np

# 根据种群大小,基因长度初始化所有的随机的染色体序列
# 后面基于这个初始化序列再不断迭代
def init(pop_size,leng):
population = []
for i in range(pop_size):
pop = ''
# 对每个位置取个初始随机01值
for j in range(leng):
pop = pop + str(np.random.randint(0,2))
population.append(pop)
return population

# 计算种群中每个个体的背包情况,返回所有个体的情况
def compute_fitness(population,weight,profit):
weight_list = []
profit_list = []
# 每一个个体是一个选物体策略
for pop in population:
tmp_w = 0
tmp_p = 0
# 遍历每个下标,看是否选了这个物体
for idx in range(len(pop)):
if pop[idx] == '1':
tmp_w += weight[idx]
tmp_p += profit[idx]
weight_list.append(tmp_w)
profit_list.append(tmp_p)
return weight_list,profit_list

# 筛选选取重量小于背包重量的
def select(weight_limit,population,weight,profit):
pop,w,p = [],[],[]
for idx in range(len(weight)):
if weight[idx] <= weight_limit:
# print(weight[idx],weight_limit)
w.append(weight[idx])
p.append(profit[idx])
pop.append(population[idx])
return pop,w,p

# 轮盘赌
def roulette(pop_size,population,total_profit):
# 计算总和
sum_profit = reduce(lambda i,j:i+j,total_profit)
# 概率即占比
p = list(map(lambda i:i/sum_profit,total_profit))
new_population = []
# 一直填充直至长度相等
while len(new_population) < pop_size:
# 选一个
tmp = np.random.choice(a=population,size=1,replace=True,p=p)
# 因为tmp是ndarray,这里转换一下
tmp = tmp.tolist()[0]
new_population.append(tmp)
return new_population

# 随机交配,返回交叉后的子代
def ga_cross(new_population,pcross):
children = []
# 每条染色体的长度
leng = len(new_population[0])
# 必须产生这么多个
while len(children) < len(new_population):
# 发生了交配
if np.random.uniform() < pcross:
# 随机选出一对父母
mo_idx = np.random.randint(0,leng)
fa_idx = np.random.randint(0,leng)
mo = new_population[mo_idx]
fa = new_population[fa_idx]
if fa_idx != mo_idx:
# 分割,交叉
seg_idx = np.random.randint(0,leng)
mo_left = mo[0:seg_idx]
mo_right = mo[seg_idx:leng]
fa_left = fa[0:seg_idx]
fa_right = fa[seg_idx:leng]
child1 = mo_left + fa_right
child2 = fa_left + mo_right
children.append(child1)
children.append(child2)
return children

# 变异
def mutation(population,pmutation):
new_pop = []
# 每条染色体长度
leng = len(population[0])
for pop in population:
if np.random.uniform() < pmutation:
# 随机一个变异位置
idx = np.random.randint(0,leng)
# 对指定下标取模
pop = list(pop)
if pop[idx] == '0':
pop[idx] = '1'
else:
pop[idx] = '0'
pop = ''.join(pop)
new_pop.append(pop)
return new_pop

if __name__ == '__main__':
pm = 0.2 # 变异概率
pc = 0.8 # 交叉概率
iters = 600 # 迭代次数
pop_size = 50 # 种群大小
n = 14 # 14个物体
# 重量
weight=[5,7,9,8,4,3,10,14,13,9,6,8,5,15]
# 价值
profit=[10,8,15,9,6,5,20,10,13,10,7,12,5,18]
# 背包大小
weight_limit = 75
# 初始化种群(pop_size个背包策略)
population = init(pop_size,n)
cur_iter = 0
ans_pop,ans_w,ans_p = [],[],[]
while cur_iter < iters:
# print(f'第{cur_iter+1}代')
# print("初始为",population)
# 计算适应度
w,p = compute_fitness(population,weight,profit)
# print('weight:',w,'profit:',p)
# 筛选选取物品总重量小于背包大小的情况
s_pop,s_w,s_p = select(weight_limit,population,w,p)
# print(f'筛选后的种群{s_pop},筛选后的veight{s_w},筛选后的profit{s_p}')
# 随机抽pop_size个人
new_pop = roulette(pop_size,s_pop,s_p)
# print(f'选择后的:{new_pop}')
new_pop1 = ga_cross(new_pop,pc)
# print(f'交叉后的:{new_pop1}')
population = mutation(new_pop1,pm)
# print(f'变异后的{population}')
cur_iter += 1
# print("-"*10)
# 记录此时种群里的最优解
# 注意要重新计算一次,因为更新了populatison
w,p = compute_fitness(population,weight,profit)
s_pop,s_w,s_p = select(weight_limit,population,w,p)
idx = s_pop.index(max(s_pop))
if len(ans_p)==0 or s_p[idx] > ans_p[-1]:
ans_pop.append(s_pop[idx])
ans_p.append(s_p[idx])
ans_w.append(s_w[idx])
# 校验一下
idx = population.index(max(population))
print("w_limit:",weight_limit)
print("w的变化",ans_w)
print("p的变化",ans_p)
# 图表展示
# 解决legend的汉字问题
plt.rcParams['font.sans-serif']=['simsun']
plt.rcParams['axes.unicode_minus'] = False
# 放数据
plt.plot(range(len(ans_p)),ans_p,label='装入物品总价值', color='red')
# 点上放数据
for idx,(w,p) in enumerate(zip(ans_w, ans_p)):
plt.text(idx, p, f'(w:{w},p:{p})')
plt.legend()
plt.show()
  • 标题: 遗传算法求解01背包问题记录
  • 作者: urlyy
  • 创建于 : 2023-06-24 14:43:04
  • 更新于 : 2023-06-30 04:22:22
  • 链接: https://urlyy.github.io/2023/06/24/遗传算法求解01背包问题记录/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
此页目录
遗传算法求解01背包问题记录