質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

3117閲覧

巡回セールスマン問題を遺伝的アルゴリズムで解きたいです。deepcopyがうまくいっていないかもしれません。

anmo

総合スコア1

Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/01/17 07:35

編集2021/01/17 08:44

前提・実現したいこと

pythonで巡回セールスマンの遺伝的アルゴリズムによる解決をしたいです。
Qiitaで見つけたプログラムを実行しているのですが、どうもエリート保存がうまくいきません。
次のループに行くときに最善の値が引き継げていないです。
どのように変更すればいいでしょうか。

教えていただければ幸いです。

発生している問題・エラーメッセージ

エリート保存がうまくいかない(突然変異によって結果が改悪されている?)

該当のソースコード

Python

1 2# coding:utf-8 3 4import random as rnd 5import matplotlib.pyplot as plt 6import math 7import copy 8 9 10def generator(num, pop_num): 11 """ 都市を初期生成 """ 12 13 # 範囲指定 14 x_range = 200 15 y_range = 200 16 17 x_coordinate = [rnd.randint(0, x_range) for _ in range(num)] 18 y_coordinate = [rnd.randint(0, y_range) for _ in range(num)] 19 20 coordinate = [[x_coordinate[i], y_coordinate[i]] for i in range(num)] 21 22 # keyが都市の番号、valueが座標値の辞書作成。 23 position_info = {} 24 for i in range(num): 25 position_info[i] = coordinate[i] 26 27 # 初期の巡回順序生成 28 select_num = [i for i in range(num)] 29 all_route = [rnd.sample(select_num, num) for _ in range(pop_num)] 30 31 return position_info, all_route 32 33 34def evaluate(position_info, all_route, loop=0): 35 """ ユークリッド距離の総和を計算し評価値とする。 """ 36 37 evaluate_value = [] 38 for i in range(len(all_route)): 39 temp_evaluate_value = [] 40 x_coordinate = [position_info[all_route[i][x]][0] for x in range(len(all_route[i]))] 41 y_coordinate = [position_info[all_route[i][y]][1] for y in range(len(all_route[i]))] 42 for j in range(len(all_route[i])): 43 if j == len(all_route[i]) - 1: 44 distance = math.sqrt( 45 pow((x_coordinate[j] - x_coordinate[0]), 2) + pow((y_coordinate[j] - y_coordinate[0]), 2)) 46 else: 47 distance = math.sqrt( 48 pow((x_coordinate[j] - x_coordinate[j + 1]), 2) + pow((y_coordinate[j] - y_coordinate[j + 1]), 2)) 49 temp_evaluate_value.append(distance) 50 evaluate_value.append(sum(temp_evaluate_value)) 51 52 # 一番優秀な個体をmatplotで描画 53 excellent_evaluate_value = min(evaluate_value) 54 draw_pop_index = evaluate_value.index(excellent_evaluate_value) 55 56 show_route(position_info, all_route[draw_pop_index],int(excellent_evaluate_value), loop=loop + 1) 57 58 return evaluate_value 59 60 61def selection(all_route, evaluate_value, tournament_select_num, tournament_size, elite_select_num, ascending=False): 62 """ トーナメント選択とエリート保存を行う""" 63 64 select_pop = [] 65 elite_pop = [] 66 # トーナメント選択 67 while True: 68 select = rnd.sample(evaluate_value, tournament_size) 69 select.sort(reverse=ascending) 70 for i in range(tournament_select_num): 71 value = select[i] 72 index = evaluate_value.index(value) 73 select_pop.append(all_route[index]) 74 75 # 個体数の半数個選択するまで実行 76 if len(select_pop) >= len(all_route) / 2: 77 break 78 79 # エリート保存 80 sort_evaluate_value = copy.deepcopy(evaluate_value) 81 sort_evaluate_value.sort(reverse=ascending) 82 for i in range(elite_select_num): 83 value = sort_evaluate_value[i] 84 index = evaluate_value.index(value) 85 elite_pop.append(all_route[index]) 86 87 return select_pop, elite_pop 88 89 90def crossover(select_pop, crossover_prob): 91 ''' 確率的に順序交叉を実行する ''' 92 93 cross_pop = rnd.sample(select_pop, 2) 94 pop_1 = cross_pop[0] 95 pop_2 = cross_pop[1] 96 97 check_prob = rnd.randint(1, 100) 98 if check_prob <= crossover_prob: 99 100 # 順序交叉 101 new_pop_1 = [] 102 cut_index = rnd.randint(1, len(pop_1) - 2) 103 new_pop_1.extend(pop_1[:cut_index]) 104 for i in range(len(pop_1)): 105 if pop_2[i] not in new_pop_1: 106 new_pop_1.append(pop_2[i]) 107 108 new_pop_2 = [] 109 new_pop_2.extend(pop_1[cut_index:]) 110 for i in range(len(pop_1)): 111 if pop_2[i] not in new_pop_2: 112 new_pop_2.append(pop_2[i]) 113 114 return new_pop_1, new_pop_2 115 116 else: 117 return pop_1, pop_2 118 119 120def mutation(pop, mutation_prob): 121 """ 循環経路の順番をランダムで入れ替える """ 122 123 check_prob = rnd.randint(0, 100) 124 125 if check_prob <= mutation_prob: 126 select_num = [i for i in range(num)] 127 select_index = rnd.sample(select_num, 2) 128 129 a = pop[select_index[0]] 130 b = pop[select_index[1]] 131 pop[select_index[1]] = a 132 pop[select_index[0]] = b 133 134 return pop 135 136 137def show_route(position_info, route, excellent_evaluate_value, loop=0): 138 """ matplotlibで循環経路を描画 """ 139 140 x_coordinate = [position_info[route[i]][0] for i in range(len(route))] 141 y_coordinate = [position_info[route[i]][1] for i in range(len(route))] 142 x_coordinate.append(position_info[route[0]][0]) 143 y_coordinate.append(position_info[route[0]][1]) 144 145 plt.scatter(x_coordinate, y_coordinate) 146 plt.plot(x_coordinate, y_coordinate, label=excellent_evaluate_value) 147 plt.title("Generation: {}".format(loop)) 148 plt.legend() 149 150 plt.savefig("tsp{}".format(loop)) # pngとして保存。カレントディレクトリにimgフォルダを置く必要あり。 151 plt.show() 152 153 154# 初期生成時のパラメータ 155num = 20 # 都市の数 156pop_num = 200 # 個体数 157generation_num = 200 # 世代数 158 159# 選択のパラメータ 160tournament_size = 10 161tournament_select_num = 2 162elite_select_num = 1 163 164# 交叉の確率 165crossover_prob = 0 166 167# 突然変異の確率 168mutation_prob = 3 169 170 171# 初期生成 172position_info, all_route = generator(num, pop_num) 173 174# 評価 175evaluate_value = evaluate(position_info, all_route) 176 177for loop in range(generation_num): 178 # 選択 179 select_pop, elite_pop = selection( 180 all_route, evaluate_value, tournament_select_num, tournament_size, elite_select_num, ascending=False) 181 182 # 選択した個体の中から2個体選択し交叉や突然変異を適用する。 183 next_pop = [] 184 while True: 185 # 交叉pop1,pop2はselectpopから選んだ 186 pop_1, pop_2 = crossover(select_pop, crossover_prob) 187 188 # 突然変異 189 190 pop_1 = mutation(pop_1, mutation_prob) 191 pop_2 = mutation(pop_2, mutation_prob) 192 193 next_pop.append(pop_1) 194 next_pop.append(pop_2) 195 196 if len(next_pop) >= pop_num - elite_select_num: 197 break 198 199 # エリート主義。優良個体を次世代へ継承。 200 next_pop.extend(elite_pop) 201 202 203 # 評価 204 evaluate_value = evaluate(position_info, next_pop, loop=loop + 1) 205 206 207 # 更新 208 all_route = next_pop

試したこと

エリート保存をする場所を変えたりしましたが、うまくいきませんでした。。。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

episteme

2021/01/17 08:24

Pythonでtabが消えるのは致命的。 コードは ```Python と ``` で挟んでください
anmo

2021/01/17 08:45

ご指摘ありがとうございます。変更いたしました。
jbpb0

2021/01/17 10:05

コードを解読するポイントを教えてください 何を確認したら、 > 次のループに行くときに最善の値が引き継げていない が分かるのか??
anmo

2021/01/17 10:23

実際に動かしたときに、世代を経て結果が悪くなってしまうんです。。。 おそらく「エリート保存」の項目か「突然変異」の項目に問題があると思っています。 next_pop.extend(elite_pop)のところで最も良い個体を引き継げているはずなのですが、そうなりません。
guest

回答1

0

ベストアンサー

交雑率(crossover_prob)がゼロとなっているのが問題なのではないでしょうか。

かつ、crossover()内で
~~ ~~check_prob = rnd.randint(1, 100)~~~~ ~~
となっているのでcrossover関数内では順序交叉が実行されません。~~

結果、短いルートを通っている個体どうしを交叉し(=ルートを組み替えて)別のルートを生成する、ということができず、
ルートの短縮化を、個体の突然変異(コードでは3%)にのみ頼ることになっていることになります。

このため、ルートを短縮化しにくい構造になっているものと思われます。

勘違いしておりました(コメントで交雑ゼロは意図的だということなので)。

loop の最後で all_route = next_popとしていますが、
selection()で、トーナメント選択のときにall_routeの要素が使われており
その後のエリート選択で、それをそのまま使っているのが原因ではないかと。

以下のように、トーナメント選択の時にいったんall_routeを別のリストにコピーして、トーナメント選出はそのコピーしたリストを利用した場合、
エリートは保存されたままとなり、最短ルートは順当に更新されていきます。

diff

1def selection(all_route, evaluate_value, tournament_select_num, tournament_size, elite_select_num, ascending=False): 2 """ トーナメント選択とエリート保存を行う""" 3 4 select_pop = [] 5 elite_pop = [] 6+ copied_all_route = copy.deepcopy(all_route) 7 # トーナメント選択 8 while True: 9 select = rnd.sample(evaluate_value, tournament_size) 10 select.sort(reverse=ascending) 11 for i in range(tournament_select_num): 12 value = select[i] 13 index = evaluate_value.index(value) 14- select_pop.append(all_route[index]) 15+ select_pop.append(copied_all_route[index]) 16 17 # 個体数の半数個選択するまで実行 18 if len(select_pop) >= len(all_route) / 2: 19 break 20 21 # エリート保存 22 sort_evaluate_value = copy.deepcopy(evaluate_value) 23 sort_evaluate_value.sort(reverse=ascending) 24 25 for i in range(elite_select_num): 26 value = sort_evaluate_value[i] 27 index = evaluate_value.index(value) 28 elite_pop.append(all_route[index]) 29 return select_pop, elite_pop

投稿2021/01/17 11:06

編集2021/01/17 12:04
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

anmo

2021/01/17 11:40

ご回答ありがとうございます。 順序交叉は意図的に起こさないようにしてしまいました。 エリート保存さえしていれば突然変異のみによる解の改善でもそれなりの答えになるのではないかと思い、交叉を利用したときとの比較のためこのような状況設定にしています。 ただ交叉をするにしても最適解が保存がうまくいっていないので、それを解決したいです。 説明不足で申し訳ありません。
退会済みユーザー

退会済みユーザー

2021/01/17 12:04

修正しました。
anmo

2021/01/17 12:25

修正ありがとうございます。 希望していた通りの動きをしてくれるようになりました。 ベストアンサーにさせていただきます。
退会済みユーザー

退会済みユーザー

2021/01/17 12:43 編集

あと突然変異率 mutation_prob を0に設定したときに、一切変化がないようにするには、 mutation()内の  if check_prob <= mutation_prob: を  if check_prob < mutation_prob: に変えてください。 (qiitaの元ソース?を今検索して見てみたところ、上記回答で修正した部分およびこの部分は修正前のままのようですが、このようにしているのは何か考えあってのことなのかもしれません)
anmo

2021/01/17 13:33

何から何までありがとうございます。 もっと勉強して自分で出来るようになれるよう頑張ります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問