这两个问题有一个共同的特点是,所有的节点都要用上,而使用遗传算法解决排序问题(每一个格子可以认为是一个节点),是需要从众多的节点之中寻找到某些节点构成一个序列X。
第二种做法的另一个问题就是随机性太大,可能会走比较长的路(其实也是可以采用的),为了解决这个问题,我才用了A*算法的启发式思维,将当前点和目标点的蔓哈顿距离作为适应度加入到优先队列中。
1 import random
2 import math
3 import copy
4 from tkinter import *
5 import tkinter.font as tkFont
6 import time, threading
7
8 WIDTH = 100
9 HEIGHT = 100
10 MIN = 0
11 MAX = WIDTH * HEIGHT - 1
12
13 PATH_COUNT = 100
14 # 交叉概率
15 cross_p = 0.6
16 # 变异概率
17 variation_p = 0.4
18 # 变异次数
19 variation_times = 4
20
21 DIS_1 = 1.4
22 DIS_2 = 1
23
24 S = 0
25 D = 0
26
27 best_path = []
28 best_path_index = 0
29
30 res_fit = []
31
32 # 路径
33 paths = []
34 # 最优路径
35 # 迭代次数
36 ITERATION_COUNT = 100
37 #
38 direction_arr = [(-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1)]
39
40
41 def is_valid(point):
42 if point[0] < 0 or point[1] < 0 or point[0] >= WIDTH or point[1] >= HEIGHT:
43 return False
44 return True
45
46
47 # 计算欧式距离
48 def distance(p1, p2):
49 return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
50
51
52 # 标号转坐标
53 def mark2position(mark):
54 return (mark % WIDTH, int(mark / WIDTH))
55
56
57 def position2mark(position):
58 return position[1] * WIDTH + position[0]
59
60
61 # 5 6 7
62 # 3 4
63 # 0 1 2
64 def generate_one_path(start, end):
65 res = []
66 res.append(start)
67
68 s = start
69 target_point = mark2position(end)
70 dis = distance(mark2position(start), target_point)
71
72 while (s != end):
73 pos = mark2position(s)
74 r = random.randint(0, 7)
75 pos = (pos[0] + direction_arr[r][0], pos[1] + direction_arr[r][1])
76 temp_dis = distance(pos, target_point)
77 if is_valid(pos) and temp_dis <= dis:
78 s = position2mark(pos)
79 dis = temp_dis
80 res.append(s)
81 return res
82
83
84 # 初代
85 def init(count):
86 res = []
87 for i in range(0, count):
88 res.append(generate_one_path(S, D))
89 return res
90
91
92 # 计算一条路径的适应度值
93 def one_path_fit_val(path):
94 sm = 0
95 for i in range(1, len(path)):
96 w = int(math.fabs(path[i - 1] - path))
97 if w == 1 or w == WIDTH:
98 sm += DIS_2
99 else:
100 sm += DIS_1
101 return MAX / sm
102
103
104 # 计算适应度值
105 def fitness():
106 res = []
107 max_fit = -1
108 global best_path
109 global best_path_index
110
111 temp_best_path = []
112
113 for i in range(len(paths)):
114 f = one_path_fit_val(paths)
115 res.append(f)
116 if f > max_fit:
117 max_fit = f
118 temp_best_path = paths
119 best_path_index = i
120 best_path = copy.deepcopy(temp_best_path)
121 res_fit.append(max_fit)
122 return res
123
124
125 # 累计概率
126 def cumulative_probability(fits):
127 res = []
128 sm = sum(fits)
129 temp = fits[0] / sm
130 res.append(temp)
131 for i in range(1, len(fits)):
132 res.append(res[i - 1] + fits / sm)
133 return res
134
135
136 # 选择 产生下一代
137 def choose(pArr, count):
138 res = []
139 for i in range(count):
140 p = random.random()
141 for j in range(len(pArr)):
142 if p <= pArr[j]:
143 res.append(paths[j])
144 break
145 return res
146
147
148 def cross_one_times(path1, path2):
149 # 求交集
150 temp = list(set(path1[1:-1]).intersection(set(path2[1:-1])))
151 sz = len(temp)
152 if sz == 0:
153 return (path1, path2)
154 r = random.random()
155 if r > cross_p:
156 index = random.randint(0, sz - 1)
157 e = temp[index]
158 t1 = path1.index(e)
159 t2 = path2.index(e)
160 p1 = path1[:t1]
161 p2 = path2[t2:]
162 p3 = path2[:t2]
163 p4 = path1[t1:]
164 p1.extend(p2)
165 p3.extend(p4)
166 return (p1, p3)
167 else:
168 return (path1, path2)
169
170
171 def cross():
172 n = len(paths)
173 res = []
174 for i in range(1, n, 2):
175 p = cross_one_times(paths, paths[i - 1])
176 res.extend(p)
177
178 # 奇数情况
179 if len(res) < n:
180 res.append(paths[n - 1])
181 return res
182
183
184 # 判断三点之间是否联通
185 def is_valid_3_mark(m1, m2, m3):
186 # 重复
187 if m1 == m2 or m1 == m3 or m2 == m3:
188 return False
189 if m2 < MIN or m2 > MAX:
190 return False
191 # 不联通
192 if not (m1 + 1 == m2 or m1 - 1 == m2 or m1 + WIDTH == m2 or m1 - WIDTH == m2
193 or m1 + WIDTH + 1 == m2 or m1 + WIDTH - 1 == m2
194 or m1 - WIDTH + 1 == m2 or m1 - WIDTH - 1 == m2):
195 return False
196 # 不联通
197 if not (m3 + 1 == m2 or m3 - 1 == m2 or m3 + WIDTH == m2 or m3 - WIDTH == m2
198 or m3 + WIDTH + 1 == m2 or m3 + WIDTH - 1 == m2
199 or m3 - WIDTH + 1 == m2 or m3 - WIDTH - 1 == m2):
200 return False
201 return True
202
203
204 def variation_one_times(path):
205 r = random.random()
206 if r < variation_p:
207 return path
208 else:
209 sz = len(path)
210 if sz <= 2:
211 return path
212 # 变异点
213 prob_mark = []
214 var_index = random.randint(1, sz - 2)
215 pre_mark = path[var_index - 1]
216 cnt_mark = path[var_index]
217 next_mark = path[var_index + 1]
218 # 8中情况
219 temp_mark = [cnt_mark + 1, cnt_mark - 1, cnt_mark + WIDTH, cnt_mark - WIDTH, cnt_mark + WIDTH + 1,
220 cnt_mark + WIDTH - 1, cnt_mark - WIDTH - 1, cnt_mark - WIDTH + 1]
221 for e in temp_mark:
222 if is_valid_3_mark(pre_mark, e, next_mark):
223 prob_mark.append(e)
224
225 if len(prob_mark) == 0:
226 return path
227 changed_mark = prob_mark[random.randint(0, len(prob_mark) - 1)]
228 path[var_index] = changed_mark
229 return path
230
231
232 def variation():
233 res = paths
234 for i in range(variation_times):
235 temp = []
236 for e in res:
237 temp.append(variation_one_times(e))
238 res = temp
239 return res
240
241
242 def output(g, f):
243 print("第" + str(g) + "代:最优路径:", end="", file=f)
244 print(best_path, end="", file=f)
245 print("适应度: ", end="", file=f)
246 print(fits[best_path_index], file=f)
247 for i, path in enumerate(paths):
248 print(str(i + 1) + ". ", end="", file=f)
249 print(path, end="", file=f)
250 print("适应度值:" + str(fits), file=f)
251
252
253 def mark_screen_position(mark, x_min, y_max):
254 temp_p = mark2position(mark)
255 x = temp_p[0] - x_min
256 y = y_max - temp_p[1]
257 return (x, y)
258
259
260 def show(path, title):
261 canvas_width = 1000
262 point_r = 2
263 show_mark_min_width = 10
264 temp = []
265 for p in path:
266 temp.append(p % 100)
267 x_min = min(temp)
268 x_max = max(temp)
269 temp.clear()
270 for p in path:
271 temp.append(int(p / 100))
272 y_min = min(temp)
273 y_max = max(temp)
274 d = max(x_max - x_min + 1, y_max - y_min + 1)
275 grid_width = int(canvas_width / d)
276 canvas_width = grid_width * d
277 win = Tk()
278 win.title(title)
279 win.geometry(str(canvas_width) + "x" + str(canvas_width) + "+100+100")
280 can = Canvas(win, width=canvas_width, height=canvas_width, bg="white")
281 for i in range(0, canvas_width, grid_width):
282 can.create_line((0, i), (canvas_width, i))
283
284 for i in range(0, canvas_width, grid_width):
285 can.create_line((i, 0), (i, canvas_width))
286 ft = tkFont.Font(root=win, family='Fixdsys', size=int(20 / 4), weight=tkFont.BOLD)
287 if grid_width >= show_mark_min_width:
288 for x in range(0, d):
289 for y in range(0, d):
290 s = position2mark((x + x_min, y_max - y))
291 can.create_text(x * grid_width + grid_width / 2, y * grid_width + grid_width / 2, text=s,
292 font=ft)
293 sz = len(path)
294 for i in range(0, sz - 1):
295 p1 = mark_screen_position(path, x_min, y_max)
296 p2 = mark_screen_position(path[i + 1], x_min, y_max)
297 can.create_line((p1[0] * grid_width + grid_width / 2, p1[1] * grid_width + grid_width / 2),
298 (p2[0] * grid_width + grid_width / 2, p2[1] * grid_width + grid_width / 2), fill="red", width=3)
299 if i == 0: {
300 can.create_oval(
301 (p1[0] * grid_width + grid_width / 2 - point_r, p1[1] * grid_width + grid_width / 2 - point_r,
302 p1[0] * grid_width + grid_width / 2 + point_r, p1[1] * grid_width + grid_width / 2 + point_r),
303 fill="blue")
304 }
305 can.create_oval((p2[0] * grid_width + grid_width / 2 - point_r, p2[1] * grid_width + grid_width / 2 - point_r,
306 p2[0] * grid_width + grid_width / 2 + point_r, p2[1] * grid_width + grid_width / 2 + point_r),
307 fill="blue")
308 can.pack()
309 win.mainloop()
310
311
312 # run point
313 random.seed()
314 S = random.randint(MIN, MAX)
315 D = random.randint(MIN, MAX)
316 while (S == D):
317 D = random.randint(MIN, MAX)
318 g = 1
319 fp = open("1.txt", "w", encoding="utf-8")
320
321 # 初代
322 paths = init(PATH_COUNT)
323 fits = fitness() # 适应度计算
324 output(g, fp)
325 g = g + 1
326
327 origin_best_path = []
328
329 for i in range(ITERATION_COUNT):
330 pArr = cumulative_probability(fits) # 累计概率
331 paths = choose(pArr, PATH_COUNT - 1) # 选择
332 paths = cross() # 交叉
333 paths = variation() # 变异
334 paths.append(best_path)
335 if i == 0:
336 origin_best_path = copy.deepcopy(best_path)
337 fits = fitness() # 适应度计算
338 output(g, fp)
339 g = g + 1
340 fp.flush()
341 fp.close()
342
343 fp = open("2.txt", "w", encoding="utf-8")
344 fp.write("最大适应度值列表:\n")
345 for e in res_fit:
346 fp.write(format(e, ".2f"))
347 fp.write(" ")
348 fp.flush()
349 fp.close()
350
351 t1 = threading.Thread(target=show, args=(origin_best_path, "初代最好的路径"))
352 t2 = threading.Thread(target=show, args=(best_path, "最好的路径"))
353 t1.start()
354 t2.start()
355 t1.join()
356 t2.join()