算法流程:
P:父辈种群
Q:子辈种群
R:P并上Q -》 之后依据偏序关系进行排序
在实际上,能在原来数组上改就到原来数组上改,要产生新的那就产生新的,分配一次内存时间应该影响不大,以后再考虑底层优化。!
在函数调用上,一律认为创建了一个新的数组
初始化:P
计算P适应度:F
根据适应度度计算层次关系:rank、L
rank为P对应的等级数组,L标记每层的元素数量
根据F、rank计算拥挤距离,越大越好:crowding_distance
更具rank, crowding_distance对dna进行排序:得到新P
对P按序号两两进行单点交叉:得Q
对Q按概率做一遍变异:新Q
将P、Q复制到R中
重复计算适应度开始重新
交叉使用单点交叉: pc = 0.9
变异概率:1/决策变量数 或 1/二进制编码长度
250代
1 # nsga2.py 2 import numpy as np 3 4 5 class individual: 6 def __init__(self, dna): 7 self.dna = dna 8 self.dna_len = len(dna) 9 10 self.f = None 11 self.rank = -1 12 self.crowding_distance = -1 # 越远 多样性越好 13 14 def __gt__(self, other): 15 if self.rank > other.rank: 16 return True 17 if self.rank == other.rank and self.crowding_distance < other.crowding_distance: 18 return True 19 return False 20 21 def get_fitness(self, F): 22 len_F = len(F) 23 fitness = np.zeros(shape=len_F) 24 for i in range(len_F): 25 fitness[i] = F[i](self.dna) 26 return fitness 27 28 def cross_newObj(self, p1, p2): 29 # 不能修改之前的,需要生成一个新的对象 30 p1_dna = p1.dna.copy() 31 p2_dna = p2.dna.copy() 32 cut_i = np.random.randint(1, self.dna_len - 1) 33 temp = p1_dna[cut_i:].copy() 34 p1_dna[cut_i:] = p2_dna[cut_i:] 35 p2_dna[cut_i:] = temp 36 37 q1 = individual(p1_dna) 38 q2 = individual(p2_dna) 39 40 return q1, q2 41 42 def cross(self, p1, p2): 43 cut_i = np.random.randint(1, self.dna_len - 1) 44 temp = p1[cut_i:].copy() 45 p1[cut_i:] = p2[cut_i:] 46 p2[cut_i:] = temp 47 return p1, p2 48 49 def mutation(self, domain): 50 point = np.random.randint(0, self.dna_len) 51 low = domain[point][0] 52 high = domain[point][1] 53 self.dna[point] = np.random.rand() * (high - low) + low 54 return self.dna 55 56 def dominate(self, p2): 57 p1 = self 58 # p1 是否支配p2 59 dominated = True 60 for i in range(len(p1.f)): 61 if p1.f[i] > p2.f[i]: 62 dominated = False 63 break 64 return dominated 65 66 67 class nsga2: 68 def __init__(self, pop_size, dna_size, pc, pm, f_funcs, domain): 69 self.pop_size = pop_size 70 self.dna_size = dna_size 71 self.f_size = len(f_funcs) 72 self.pc = pc 73 self.pm = pm 74 self.f_funcs = f_funcs 75 self.domain = domain 76 77 def ini_pop(self): 78 P_dna = np.random.random(size=(self.pop_size, self.dna_size)) 79 for i in range(len(self.domain)): 80 item = self.domain[i] 81 P_dna[:, i] = P_dna[:, i] * (item[1] - item[0]) + item[0] 82 83 P = [] # 初始种群P 84 for dna in P_dna: 85 P.append(individual(dna)) 86 87 return P 88 def calculate_pop_fitness(self, pop): 89 # 计算Pop适应度,存储到individual中 90 for p in pop: 91 p.f = p.get_fitness(self.f_funcs) 92 return pop 93 94 def fast_non_dominated_sort(self, R): 95 # 非支配排序操作 96 # 默认调用之前,改执行的动作已经执行过了 97 R_len = len(R) 98 n = np.zeros(shape=R_len) # 被支配数 99 S = [] # 支配了那些成员 100 F = [[]] # 每层个体编号(对R) 101 for i in range(R_len): 102 Sp = [] 103 n[i] = 0 104 for j in range(R_len): 105 if i == j: 106 continue 107 if R[i].dominate(R[j]): 108 Sp.append(j) 109 elif R[j].dominate(R[i]): 110 n[i] += 1 111 S.append(Sp) 112 if n[i] == 0: 113 R[i].rank = 0 114 F[0].append(i) 115 116 i = 0 117 while len(F[i]) != 0: 118 Q = [] # i+1层 119 for p in F[i]: 120 for q in S[p]: 121 n[q] -= 1 122 if n[q] == 0: 123 R[q].rank = i + 1 124 Q.append(q) 125 i += 1 126 F.append(Q) 127 F.pop() 128 129 # 转化为实际的人 130 F_obj = [] 131 for rank in F: 132 rank_obj = [R[p_idx] for p_idx in rank] 133 F_obj.append(rank_obj) 134 135 return F_obj # 每层包含的实际的人 136 137 def crowding_distance_pop(self, F): 138 for rank in F: 139 self.crowding_distance_assignment(rank) 140 141 def crowding_distance_assignment(self, rank): 142 # 初始化该组所有人的距离为0 143 pop_fitness = [] 144 for p in rank: 145 p.crowding_distance = 0 146 pop_fitness.append(p.f) 147 pop_fitness = np.array(pop_fitness) 148 sorted_idx = np.argsort(pop_fitness, axis=0) 149 f_dimension = len(self.f_funcs) 150 for i in range(f_dimension): 151 # 在f1下是适应度的排序 152 fi_sort_idx = sorted_idx[:, i] 153 fi_sort_p = [rank[i] for i in fi_sort_idx] 154 # fi_sort_p = rank[fi_sort_idx] 155 inf = 9999999999999 156 fi_sort_p[0].crowding_distance = fi_sort_p[-1].crowding_distance = float(inf) 157 diff = fi_sort_p[-1].f[i] - fi_sort_p[0].f[i] 158 for j in range(1, len(fi_sort_p) - 1): 159 a = fi_sort_p[j + 1].f[i] 160 # fi_sort_p[j].crowding_distance += (fi_sort_p[j + 1].f[i] - fi_sort_p[j - 1].f[i]) / diff 161 fi_sort_p[j].crowding_distance += (fi_sort_p[j + 1].f[i] - fi_sort_p[j - 1].f[i]) 162 163 # 打印看下 164 return rank 165 166 def select(self, R): 167 R.sort() 168 # print(len(R)) 169 # print([[p.rank, p.crowding_distance] for p in R]) 170 return R[:self.pop_size] 171 172 def pop_cross(self, P): 173 new_Q = [] 174 i = 0 175 P_len = len(P) 176 while i < P_len: 177 q1, q2 = P[i].cross_newObj(P[i], P[i + 1]) 178 new_Q.append(q1) 179 new_Q.append(q2) 180 i += 2 181 return new_Q 182 183 def pop_mutation(self, Q): 184 for q in Q: 185 if np.random.rand() < self.pm: 186 q.mutation(self.domain) 187 return Q
1 # zdt_funcs.py
2 import numpy as np
3
4
5 def zdt4_f1(x_list):
6 return x_list[0]
7
8
9 def zdt4_gx(x_list):
10 sum = 1 + 10 * (10 - 1)
11 for i in range(1, 10):
12 sum += x_list[i] ** 2 - 10 * np.cos(4 * np.pi * x_list[i])
13 return sum
14
15
16 def zdt4_f2(x_list):
17 gx_ans = zdt4_gx(x_list)
18 if x_list[0] < 0:
19 print("????: x_list[0] < 0:", x_list[0])
20 if gx_ans < 0:
21 print("gx_ans < 0", gx_ans)
22 if (x_list[0] / gx_ans) 0:
23 print("x_list[0] / gx_ans", x_list[0] / gx_ans)
24
25 ans = 1 - np.sqrt(x_list[0] / gx_ans)
26 return ans
27
28
29 def zdt3_f1(x):
30 return x[0]
31
32
33 def zdt3_gx(x):
34 if x[:].sum() < 0:
35 print(x[1:].sum(), x[1:])
36 ans = 1 + 9 / 29 * x[1:].sum()
37 return ans
38
39
40 def zdt3_f2(x):
41 g = zdt3_gx(x)
42 ans = 1 - np.sqrt(x[0] / g) - (x[0] / g) * np.sin(10 * np.pi * x[0])
43 return ans
44
45
46 def get_zdt(name="zdt4"):
47 if name == "zdt4":
48 f_funcs = [zdt4_f1, zdt4_f2]
49 domain = [[0, 1]]
50 for i in range(9):
51 domain.append([-5, 5])
52 return f_funcs, domain
53
54 if name == "zdt3":
55 f_funcs = [zdt3_f1, zdt3_f2]
56 domain = [[0, 1] for i in range(30)]
57 return f_funcs, domain
1 # 实现zdt.py
2 import numpy as np
3 from nsga2 import *
4 import matplotlib.pyplot as plt
5 from zdt_funcs import *
6
7
8 # 画图
9 def draw(P: object) -> object:
10 for t in P:
11 # 每level
12 x = [p.f[0] for p in P]
13 y = [p.f[1] for p in P]
14 # plt.clf()
15 plt.xlabel("f0")
16 plt.ylabel("f1")
17 plt.scatter(x, y, s=5) # s 点的大小 c 点的颜色 alpha 透明度
18
19 plt.show()
20
21
22 def draw_rank(F):
23 for t in F:
24 # 每level
25 x = [p.f[0] for p in t]
26 y = [p.f[1] for p in t]
27 plt.scatter(x, y, s=15) # s 点的大小 c 点的颜色 alpha 透明度
28
29 plt.show()
30
31
32 # 准备目标问题
33
34
35 f_funcs, domain = get_zdt("zdt3")
36 # 准备参数
37 pop_size = 300
38 dna_size = len(domain)
39 pm = 0.2
40
41 # 开始操作
42 nsga2 = nsga2(pop_size, dna_size, pc=1, pm=0.7
43 , f_funcs=f_funcs, domain=domain)
44
45 # x1 [0,1] xi [-5,5]
46 P = nsga2.ini_pop()
47 # 计算初始适应度
48 nsga2.calculate_pop_fitness(P)
49 # 进行非支配排序
50 F = nsga2.fast_non_dominated_sort(P)
51 # 计算拥挤距离
52 nsga2.crowding_distance_pop(F)
53 R = P
54
55 N = 100
56 for i in range(N):
57 print(i)
58 # 选择
59 P = nsga2.select(R)
60 # 交叉
61 Q = nsga2.pop_cross(P)
62 # 变异
63 Q = nsga2.pop_mutation(Q)
64 # 合并
65 R = P + Q
66 # 计算适应度
67 nsga2.calculate_pop_fitness(R)
68 # 非支配排序
69 F = nsga2.fast_non_dominated_sort(R)
70 # 画个图
71 # draw_rank(F)
72 # 计算拥挤距离
73 nsga2.crowding_distance_pop(F)
74
75 P = nsga2.select(R)
76 # 画图
77 draw(P)
实现效果:
增加种群数量以及迭代次数,能够使曲线更加的完美。我电脑i3 10105f。在代码中参数的配置下,能够很快运行完成。
Original: https://www.cnblogs.com/Twobox/p/16416460.html
Author: Wei_Xiong
Title: NSGA2、NSGA-II实现、基于分配的多目标进化-Python
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/805093/
转载文章受原作者版权保护。转载请注明原作者出处!