回答編集履歴

1

誤植修正とコード呈示

2019/10/06 00:51

投稿

退会済みユーザー
test CHANGED
@@ -42,16 +42,12 @@
42
42
 
43
43
  参照先URLに`pop_num`が個体数とありますので、`pop_num =200 # 個体数`を201などに書き換えたら良さそうですね。
44
44
 
45
-
46
-
47
45
  ――もしくは、巡回先の都市の数ですか?もしそうであれば、
48
46
 
49
47
  `num = 100 # 都市の数`
50
48
 
51
49
  ここの数値が4では3に、それ以下だと動作せず(2つなら経路は触りようなし、1つなら巡回できず)。100では99になりますね。
52
50
 
53
-
54
-
55
51
  2. 初期位置の件
56
52
 
57
53
  `position_info, all_route = generator(num, pop_num)`
@@ -61,3 +57,513 @@
61
57
  具体的には、既定では200x200のマス目に対してランダムに都市の位置(座標)を数値を割り振って、
62
58
 
63
59
  いるようですので、`x_coordinate`と`y_coordinate`を決め打ちしたら良さそうです。
60
+
61
+
62
+
63
+ ---
64
+
65
+ ```Python3
66
+
67
+ # coding:utf-8
68
+
69
+
70
+
71
+ import random as rnd
72
+
73
+ import matplotlib.pyplot as plt
74
+
75
+ import math
76
+
77
+ import copy
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+ '''
86
+
87
+ 引数はnumが地点の数。pop_numが個体数です。
88
+
89
+ 初期に地点をランダムに作成し、keyを地点番号、座標値をvalueとして辞書を作成しています。
90
+
91
+ 今回は地点番号の並び順を遺伝子配列としてみなし、アルゴリズムを構築していきます。
92
+
93
+ はじめの巡回経路はランダムに作成しています。
94
+
95
+ '''
96
+
97
+
98
+
99
+ def generator(num, pop_num):
100
+
101
+ """ 都市を初期生成 """
102
+
103
+
104
+
105
+ # 範囲指定
106
+
107
+ x_range = 200
108
+
109
+ y_range = 200
110
+
111
+
112
+
113
+ x_coordinate = [rnd.randint(0, x_range) for _ in range(num-1)] #初期位置を別に作るので"num-1"
114
+
115
+ y_coordinate = [rnd.randint(0, y_range) for _ in range(num-1)]
116
+
117
+
118
+
119
+ print(x_coordinate )
120
+
121
+ print(y_coordinate )
122
+
123
+
124
+
125
+ coordinate = [[x_coordinate[i], y_coordinate[i]] for i in range(num-1)]
126
+
127
+
128
+
129
+ # 初期位置(トラクタ置き場)生成
130
+
131
+ first_position = [rnd.randint(0, x_range),rnd.randint(0, y_range)]
132
+
133
+
134
+
135
+ # keyが都市の番号、valueが座標値の辞書作成。
136
+
137
+ position_info = {}
138
+
139
+
140
+
141
+ # IndexError: list index out of range
142
+
143
+ ######################################
144
+
145
+ # for i in range(num):
146
+
147
+ for i in range(num-1):
148
+
149
+ position_info[i] = coordinate[i]
150
+
151
+
152
+
153
+ # 初期の巡回順序生成
154
+
155
+ select_num = [i for i in range(num-1)]
156
+
157
+ all_route = [rnd.sample(select_num, num-1) for _ in range(pop_num)]
158
+
159
+
160
+
161
+ # 1つ目と2つ目のルートの長さ
162
+
163
+
164
+
165
+
166
+
167
+ return position_info, all_route
168
+
169
+
170
+
171
+
172
+
173
+ '''
174
+
175
+ ここでは各座標のユークリッド距離の総和を評価値として評価関数を設計しています。
176
+
177
+ show_route()で現世代における最良個体を描画しています。
178
+
179
+ '''
180
+
181
+
182
+
183
+ def evaluate(position_info, all_route, loop=0):
184
+
185
+ """ ユークリッド距離の総和を計算し評価値とする。 """
186
+
187
+
188
+
189
+ evaluate_value = []
190
+
191
+ for i in range(len(all_route)):
192
+
193
+ temp_evaluate_value = []
194
+
195
+ x_coordinate = [position_info[all_route[i][x]][0] for x in range(len(all_route[i]))]
196
+
197
+ y_coordinate = [position_info[all_route[i][y]][1] for y in range(len(all_route[i]))]
198
+
199
+ for j in range(len(all_route[i])):
200
+
201
+ if j == len(all_route[i]) - 1:
202
+
203
+ distance = math.sqrt(
204
+
205
+ pow((x_coordinate[j] - x_coordinate[0]), 2) + pow((y_coordinate[j] - y_coordinate[0]), 2))
206
+
207
+ else:
208
+
209
+ distance = math.sqrt(
210
+
211
+ pow((x_coordinate[j] - x_coordinate[j + 1]), 2) + pow((y_coordinate[j] - y_coordinate[j + 1]), 2))
212
+
213
+ temp_evaluate_value.append(distance)
214
+
215
+ evaluate_value.append(sum(temp_evaluate_value))
216
+
217
+
218
+
219
+ # 一番優秀な個体をmatplotで描画
220
+
221
+ excellent_evaluate_value = min(evaluate_value)
222
+
223
+ draw_pop_index = evaluate_value.index(excellent_evaluate_value)
224
+
225
+
226
+
227
+ show_route(position_info, all_route[draw_pop_index],int(excellent_evaluate_value), loop=loop + 1)
228
+
229
+
230
+
231
+ return evaluate_value
232
+
233
+
234
+
235
+
236
+
237
+ '''
238
+
239
+ 今回は選択として、トーナメント選択、エリート主義を導入しています。
240
+
241
+ トーナメント選択は指定したサイズのトーナメントを作成し、その中で指定数の優良個体を選択します。
242
+
243
+ エリート主義は指定した上位個体を問答無用で次世代に残す手法です。
244
+
245
+ '''
246
+
247
+
248
+
249
+ def selection(all_route, evaluate_value, tournament_select_num, tournament_size, elite_select_num, ascending=False):
250
+
251
+ """ トーナメント選択とエリート保存を行う"""
252
+
253
+
254
+
255
+ select_pop = []
256
+
257
+ elite_pop = []
258
+
259
+ # トーナメント選択
260
+
261
+ while True:
262
+
263
+ select = rnd.sample(evaluate_value, tournament_size)
264
+
265
+ select.sort(reverse=ascending)
266
+
267
+ for i in range(tournament_select_num):
268
+
269
+ value = select[i]
270
+
271
+ index = evaluate_value.index(value)
272
+
273
+ select_pop.append(all_route[index])
274
+
275
+
276
+
277
+ # 個体数の半数個選択するまで実行
278
+
279
+ if len(select_pop) >= len(all_route) / 2:
280
+
281
+ break
282
+
283
+
284
+
285
+ # エリート保存
286
+
287
+ sort_evaluate_value = copy.deepcopy(evaluate_value)
288
+
289
+ sort_evaluate_value.sort(reverse=ascending)
290
+
291
+ for i in range(elite_select_num):
292
+
293
+ value = sort_evaluate_value[i]
294
+
295
+ index = evaluate_value.index(value)
296
+
297
+ elite_pop.append(all_route[index])
298
+
299
+
300
+
301
+ return select_pop, elite_pop
302
+
303
+
304
+
305
+ '''
306
+
307
+ 指定した確率によって交叉を実行します。
308
+
309
+ ここでは順序交叉と呼ばれる交叉手法を使用しています。
310
+
311
+ '''
312
+
313
+ def crossover(select_pop, crossover_prob):
314
+
315
+ ''' 確率的に順序交叉を実行する '''
316
+
317
+
318
+
319
+ cross_pop = rnd.sample(select_pop, 2)
320
+
321
+ pop_1 = cross_pop[0]
322
+
323
+ pop_2 = cross_pop[1]
324
+
325
+
326
+
327
+ check_prob = rnd.randint(0, 100)
328
+
329
+ if check_prob <= crossover_prob:
330
+
331
+
332
+
333
+ # 順序交叉
334
+
335
+ new_pop_1 = []
336
+
337
+ cut_index = rnd.randint(1, len(pop_1) - 2)
338
+
339
+ new_pop_1.extend(pop_1[:cut_index])
340
+
341
+ for i in range(len(pop_1)):
342
+
343
+ if pop_2[i] not in new_pop_1:
344
+
345
+ new_pop_1.append(pop_2[i])
346
+
347
+
348
+
349
+ new_pop_2 = []
350
+
351
+ new_pop_2.extend(pop_1[cut_index:])
352
+
353
+ for i in range(len(pop_1)):
354
+
355
+ if pop_2[i] not in new_pop_2:
356
+
357
+ new_pop_2.append(pop_2[i])
358
+
359
+
360
+
361
+ return new_pop_1, new_pop_2
362
+
363
+
364
+
365
+ else:
366
+
367
+ return pop_1, pop_2
368
+
369
+
370
+
371
+ '''
372
+
373
+ 指定した確率で突然変異が起こるようにしています。
374
+
375
+ 今回は地点番号を入れ替える操作を突然変位としています。
376
+
377
+ '''
378
+
379
+ def mutation(pop, mutation_prob):
380
+
381
+ """ 循環経路の順番をランダムで入れ替える """
382
+
383
+
384
+
385
+ check_prob = rnd.randint(0, 100)
386
+
387
+
388
+
389
+ if check_prob <= mutation_prob:
390
+
391
+ # IndexError: list index out of range
392
+
393
+ # select_num = [i for i in range(num)]
394
+
395
+ select_num = [i for i in range(num-1)]
396
+
397
+ select_index = rnd.sample(select_num, 2)
398
+
399
+
400
+
401
+ a = pop[select_index[0]]
402
+
403
+ b = pop[select_index[1]]
404
+
405
+ pop[select_index[1]] = a
406
+
407
+ pop[select_index[0]] = b
408
+
409
+
410
+
411
+ return pop
412
+
413
+
414
+
415
+ '''
416
+
417
+ matplotlibを使用して経路を描画する関数です。
418
+
419
+ plt.savefig()は描画したグラフをpngとして保存するためのものです。
420
+
421
+ '''
422
+
423
+ def show_route(position_info, route, excellent_evaluate_value, loop=0):
424
+
425
+ """ matplotlibで循環経路を描画 """
426
+
427
+
428
+
429
+ x_coordinate = [position_info[route[i]][0] for i in range(len(route))]
430
+
431
+ y_coordinate = [position_info[route[i]][1] for i in range(len(route))]
432
+
433
+ x_coordinate.append(position_info[route[0]][0])
434
+
435
+ y_coordinate.append(position_info[route[0]][1])
436
+
437
+
438
+
439
+ plt.figure() #これ入れたら重ね書きされなくなった。
440
+
441
+
442
+
443
+ plt.scatter(x_coordinate, y_coordinate)
444
+
445
+ plt.plot(x_coordinate, y_coordinate, label=excellent_evaluate_value)
446
+
447
+ plt.title("Generation: {}".format(loop))
448
+
449
+ plt.legend()
450
+
451
+
452
+
453
+ # " FileNotFoundError: [Errno 2] No such file or directory: 'C:\Users\....'
454
+
455
+ # plt.savefig("C:\Users\241k\Documents\img\tsp{}".format(loop)) # pngとして保存。カレントディレクトリにimgフォルダを置く必要あり。
456
+
457
+ plt.savefig("./img/tsp{}".format(loop))
458
+
459
+ # plt.show()
460
+
461
+
462
+
463
+ # RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory.
464
+
465
+ plt.close()
466
+
467
+
468
+
469
+ # 初期生成時のパラメータ
470
+
471
+ num = 100 # 都市の数
472
+
473
+ pop_num =200 # 個体数
474
+
475
+ generation_num = 100 # 世代数
476
+
477
+
478
+
479
+ # 選択のパラメータ
480
+
481
+ tournament_size = 10 # トーナメントサイズ
482
+
483
+ tournament_select_num = 2 # トーナメントサイズで選択する個体数
484
+
485
+ elite_select_num = 1 # エリート主義で選択する個体数
486
+
487
+
488
+
489
+ # 交叉の確率
490
+
491
+ crossover_prob = 50
492
+
493
+
494
+
495
+ # 突然変異の確率
496
+
497
+ mutation_prob = 3
498
+
499
+
500
+
501
+ # 初期生成
502
+
503
+ position_info, all_route = generator(num, pop_num)
504
+
505
+
506
+
507
+ # 評価
508
+
509
+ evaluate_value = evaluate(position_info, all_route)
510
+
511
+
512
+
513
+ for loop in range(generation_num):
514
+
515
+ # 選択
516
+
517
+ select_pop, elite_pop = selection(
518
+
519
+ all_route, evaluate_value, tournament_select_num, tournament_size, elite_select_num, ascending=False)
520
+
521
+
522
+
523
+ # 選択した個体の中から2個体選択し交叉や突然変異を適用する。
524
+
525
+ next_pop = []
526
+
527
+ while True:
528
+
529
+ # 交叉
530
+
531
+ pop_1, pop_2 = crossover(select_pop, crossover_prob)
532
+
533
+ # 突然変異
534
+
535
+ pop_1 = mutation(pop_1, mutation_prob)
536
+
537
+ pop_2 = mutation(pop_2, mutation_prob)
538
+
539
+
540
+
541
+ next_pop.append(pop_1)
542
+
543
+ next_pop.append(pop_2)
544
+
545
+
546
+
547
+ if len(next_pop) >= pop_num - elite_select_num:
548
+
549
+ break
550
+
551
+
552
+
553
+ # エリート主義。優良個体を次世代へ継承。
554
+
555
+ next_pop.extend(elite_pop)
556
+
557
+
558
+
559
+ # 評価
560
+
561
+ evaluate_value = evaluate(position_info, next_pop, loop=loop + 1)
562
+
563
+
564
+
565
+ # 更新
566
+
567
+ all_route = next_pop
568
+
569
+ ```