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

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

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

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

Q&A

1回答

4418閲覧

pythonで巡回セールマン問題を遺伝的アルゴリズムで解く方法

okazujet

総合スコア11

Python

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

1グッド

0クリップ

投稿2017/03/05 06:25

編集2017/03/05 11:13

###前提・実現したいこと
巡回セールスマン問題を遺伝的アルゴリズムでpythonを使って解く方法について
質問したいです。
参考URL
http://blog.livedoor.jp/komq/archives/1012592617.html

要は2地点間の移動時間がすべて計算されている上での
遺伝的アルゴリズムを用いた巡回セールスマン問題を解ける方法を知りたいですが、
以下長文でやりたいことを書きます。

現在npyファイルに
地点間の移動時間[秒]を
[[定義なし,2.63,6.52,5.34],
[2.63,定義なし,5.87,4.32],
[6.52,5.87,定義なし,7.87],
[5.34,5.87,7.87,定義なし]]
のように格納しているファイルを持っています。
地点数は全部で150地点ほどあり、
それぞれ[shop1,shop2,....,shop150]のように
IDとして取り込んでいます。

やりたいこととしては、
・ユーザごとに移動パターンを作成する
・ユーザごとにアンケート結果をもっており
user = [(shop28,1),(shop70,1),(shop71,2),(shop72,1),(shop83,2),(shop86,2),(shop99,1),(shop123,2),(shop133,2),(shop144,2)]のように各地点に対する興味度合いが格納されており、回答されていない地点は興味がないということで訪問する可能性が0としています。
1と2でその地点に滞在する時間を変えており、それぞれtb = 180,tw = 600のように設定できるようにしています。
・訪問する制限時間をT=7200のように設定しており、その時間内で滞在時間と移動時間を含めて移動パターンを作成します。
・アンケート結果で1,2と回答しているお店のどこかに行くものとします。
回答数が10店舗であれば10C10+10C9+10C8+・・・・+10C1だけ巡回セールスマンを解いて
かかった時間と順路を1行目からExcelファイルに吐き出せるようにしたいです。

現在巡回セールスマン問題を総当たりで解いており
10店舗であれば2分ぐらいで修了するのですが、
20店舗になると何年もかかる計算になっており困っています。
ご協力お願いします。
不明瞭な点があれば質問をお願いします。

python

1# -*- coding: utf-8 -*- 2import math 3import itertools 4import queue 5import numpy as np 6import csv 7 8def compute_travel_time_between2shops(shop1, shop2): 9 start_node = "" 10 goal_node = "" 11 shop1_node_distance = 0 12 shop2_node_distance = 0 13 14 #shop1とshop2には,Nodeも許すので,条件分岐する 15 if isinstance(shop1, Node) and isinstance(shop2, Node): 16 start_node = shop1 17 goal_node = shop2 18 elif isinstance(shop1, Node): 19 start_node = shop1 20 goal_node = shop2.node 21 shop2_node_distance = math.sqrt((shop2.x - goal_node.x)**2 + (shop2.y - goal_node.y)**2) 22 elif isinstance(shop2, Node): 23 start_node = shop1.node 24 goal_node = shop2 25 shop1_node_distance = math.sqrt((shop1.x - start_node.x)**2 + (shop1.y - start_node.y)**2) 26 else: 27 start_node = shop1.node 28 goal_node = shop2.node 29 shop1_node_distance = math.sqrt((shop1.x - start_node.x)**2 + (shop1.y - start_node.y)**2) 30 shop2_node_distance = math.sqrt((shop2.x - goal_node.x)**2 + (shop2.y - goal_node.y)**2) 31 32 result_time = shop1_node_distance + shop2_node_distance 33 34 if start_node == goal_node: 35 return result_time 36 37 searched_nodes = [] 38 unsearched_nodes = [] 39 40 start_node = Neo_Node(start_node) 41 unsearched_nodes.append(start_node) 42 43 while True: 44 if len(unsearched_nodes) == 0: 45 break 46 47 #確定nodeを一つ決める 48 done_node = "" 49 temp_cost = 100000 50 for un_n in unsearched_nodes: 51 if un_n.cost < temp_cost: 52 done_node = un_n 53 temp_cost = un_n.cost 54 55 #決まったら入れ替え 56 searched_nodes.append(done_node) 57 unsearched_nodes.remove(done_node) 58 59 #done_nodeに繋がるnodeを未確定に入れる 60 for (edge, cost) in zip(done_node.edges_to, done_node.edges_cost): 61 new_edge = Neo_Node(edge) 62 new_edge.cost = done_node.cost + cost 63 64 #x,y が同じnodeがsearchedに入ってたら入れない 65 canInsert = True 66 for node in searched_nodes: 67 if node.x == edge.x and node.y == edge.y: 68 canInsert = False 69 70 #x,yが同じnodeがunsearchedに入ってたらcostを比較する 71 for node in unsearched_nodes: 72 if node.x == edge.x and node.y == edge.y and new_edge.cost < node.cost: 73 unsearched_nodes.remove(node) 74 75 if canInsert and new_edge.floor == done_node.floor: 76 unsearched_nodes.append(new_edge) 77 78 79 #goal_nodeと同じヤツを出力 80 for searched_node in searched_nodes: 81 if searched_node.x == goal_node.x and searched_node.y == goal_node.y: 82 result_time += searched_node.cost 83 84 return result_time * multiple_value(shop1.floor) 85 86#ノードの階数、x座標、y座標を記録 87 #nosが廊下にある分岐点、elsがエレベーターノード 88 nos201 = Node(2,253,273) 89 90#Shopの店名、カテゴリ、階数、x座標、y座標、リンクを持つノードを記録,本当は他にもショップの定義があります。 91 shop2 = Shop(2, " URBAN RESEARCH DOORS", "レディス・メンズ・キッズ・雑貨",4,263,235,nos403) 92 93# 配列読み込み 94 granfront = np.load("a.npy") 95 96 user = [(shop28,1),(shop70,2),(shop71,2),(shop72,2),(shop83,2),(shop86,2),(shop99,1),(shop123,2),(shop133,2),(shop144,2)] 97 98 limit_time = 7200 99 tb = 180 # 1 100 tw = 600 # 2 101 102 for i in range(len(user)): 103 shop_combinations = list(itertools.combinations(user, len(user)-i)) 104 for shop_comb in shop_combinations: 105 shop_permutations = list(itertools.permutations(shop_comb)) 106 result_time = 100000 107 result_root = [] 108 for shop_perm in shop_permutations: 109 temp_time = 0 110 temp_shops = [] 111 for sh, like in shop_perm: 112 temp_shops.append(sh) 113 if like == 1: 114 temp_time += tb 115 else: 116 temp_time += tw 117 118 temp_time += compute_travel_time_between2shops(start_node, temp_shops[0]) 119 previous_node = temp_shops[0] 120 for sh in temp_shops: 121 if previous_node == sh: 122 continue 123 124 temp_time += granfront[sh.id][previous_node.id] 125 previous_node = sh 126 127 if temp_time < result_time: 128 result_time = temp_time 129 result_root = temp_shops 130 131 # 時間がlimit_timeより少ないなら時間と順番書き込み 132 if result_time <= limit_time: 133 print("制限時間内にいける店舗の順番と時間:" + str(result_time)) 134 with open("user1.csv", "a") as f: 135 result = [] 136 result.append(result_time) 137 for sh in result_root: 138 result.append(sh.name) 139 140 writer = csv.writer(f, lineterminator='\n') 141 writer.writerow(result) 142
hon.ki👍を押しています

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

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

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

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

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

guest

回答1

0

遺伝的アルゴリズムの概要はwikipediaなどを参照すると基本的な考え方が書いてあります。本件でいえば「適当に作った順路の候補をN組作り、ある順路に対する評価関数を決め(ここでは総巡回時間になるでしょうか)、そのN組の順路をすこしずつ変えながらよりスコアの高い個体が遺伝的に生み出されるのを一定回数世代交代を繰り返しながら待つ」という感じになると思います。

残念ながら「このレベルの設問条件ならこういう手順でやればよい」と言えるほど自分には知識がないので具体的な方針をコメントすることはできません。自分の認識ではどんな問題でも効率よく解ける決定版アルゴリズムがあるわけではなく、いまだ色々な観点で研究が続いていると思います。例えば「より評価点の高い個体を生み出すためにどういうふうに子孫を生成するか・残すかの方針」についてバリエーションがあると思います。ご覧のページのコードには「エリート戦略、トーナメント方式」を採用しているようです。

ご覧のページにはコンパクトかつ的確なコメントが添えられたコードが提示されていると思います。これを参考にしてとりあえず作成してみてはいかがでしょうか?そのページの説明以上の回答はもはやコードそのものになってしまう気がしますよ?

投稿2017/03/05 08:17

KSwordOfHaste

総合スコア18394

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

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

okazujet

2017/03/05 08:45

ご回答ありがとうございます。 長文を読んでくださりありがとうございます。 やりたいことは 「適当に作った順路の候補をN組作り、ある順路に対する評価関数を決め(ここでは総巡回時間になるでしょうか)、そのN組の順路をすこしずつ変えながらよりスコアの高い個体が遺伝的に生み出されるのを一定回数世代交代を繰り返しながら待つ」でおっしゃる通りで 考え方も参考URLや遺伝的アルゴリズムについて調べたらやり方自体は把握していますが、 実装力がなくコードとして読み込むことができません。 全ての地点を座標化し、それをダイクストラ法を用いてそれらの移動時間をすべて算出できたのですが、 遺伝的アルゴリズムを用いて巡回セールスマン問題を解くコードを実装できなくて困っています。 入力に全地点のうち数地点の地点名と突然変異させる回数を入力すれば 出力でそう移動時間と巡回経路を算出プログラムをいただけないでしょうか? ご迷惑をおかけしてしまい申し訳ございません 色々調べたのですが、pythonで今回の事例にあって改変すればできそうなソースコードを見つけることができませんでした。 shop17,-999,80.25991779,-999,102.6312653,38.85243401,90.38458331,95.96488901,64.29904977,90.13840462,169.2105062,100.8549304,81.1001744,-999,129.2528464,70.01976573,79.12109002,-999,59.54952577,42.89306815,110.2578754,72.58839286,42.14606695,69.15961034,80.36088935,-999,86.59051634,117.1357507,89.79788987,47.25822572,37.80504901,107.2621976,65.51107772,93.41204033,88.02187531,107.8455901,89.09396394,58.91324991,61.35604599,174.8561111,179.3337755,208.189624,72.59666839,116.7631609,95.83544898,33.90504901,143.6118272,71.95948506,109.6254799,116.1701086,102.1972885,85.52439506,97.99480664,177.7553448,75.20473419,117.1357507,94.82737996,97.14282549,106.0026035,109.692019,133.3339339,73.18281811,11.27302945,143.6118272,93.08610402,91.31618042,61.8555755,116.9041275,105.4912831,-999,84.87977427,108.0478082,103.691561,109.4896892,82.4633531,112.8688618,79.38566535,95.39176162,-999,198.2178752,189.9557005,-999,92.71503182,75.44273092,127.112207,-999,73.13584563,158.1807651,208.0733379,86.66678231,108.9669302,34.80273552,192.9648701,-999,10.3385264,45.48305185,72.23075118,105.008329,108.9226273,30.83519602,-999,105.3797964,46.9459517,30.30458959,77.91912071,135.3088376,105.4104545,-999,101.6567447,112.7110348,57.7081276,201.1092368,-999,73.37767136,76.80635101,-999,-999,83.2149378,86.78735277,-999,98.18618616,-999,132.7741854,133.3721241,70.80443473,-999,-999,95.33517294,73.82161244,88.27771008,84.33052141,60.75769615,73.96051447,73.37220649,-999,-999,92.17771008,201.1592995,111.9864345,87.39042384,24.44674866,86.81649406,33.64485341,86.16661417,73.67502834,122.4544955,-999,-999,-999,83.13574494,-999,196.2458269,77.55813647,141.0074767,80.56804304,-999,84.13626638,-999,-999 全ての地点についてほかの地点との移動時間をこのように格納しています。 -999となっているのは定義はしているが、行く必要がない地点との移動時間を仮に設定しています。
KSwordOfHaste

2017/03/05 12:41 編集

自分の場合は興味があるのでできたらもう少し具体的アドバイスもしたいのですが、如何せん30分やそこらでは充分な回答は無理なので、残念ながらコードによるアドバイスはできそうもありません。 同じ問題を共有していたり興味をお持ちの方がいらっしゃればコードそのものの回答がつく可能性はあると思います。おわかりと思いますが本サイトは無償の助け合いサイトです。よって難易度・複雑さが大きくなるほど具体的なコードによる回答を得られる可能性は低くなると思います。回答に必要な手間が大きくなりますから。 ただ、ある程度まで完成に近づいたコードを提示できるなら、アドバイスが得られる可能性はずっと高くなると思います。 なお、長大なデータ・コードはコメント欄には書かずに質問欄に見やすい形で書きましょう。閲覧者が見やすくなりそれだけ回答の手間が減るからです。
okazujet

2017/03/05 11:30

サービスの使い方からご教示いただきありがとうございます。 初めてこのサービスを利用したのでありがたいです。 もう少し次回から抽象的な質問にします。 一応コードを最低限にして質問覧に載せました。 できればご回答いただけると幸いです。
can110

2017/03/05 11:41

パッと見てナップサック問題にも見えますね。 店舗内の滞留時間を考慮(単純加算)した巡回セールスマン問題としても解けそうですが。
okazujet

2017/03/05 12:08

>can110さん そうだと思います。 解き方に困っているのですが、 何か簡単な解き方はないでしょうか?
KSwordOfHaste

2017/03/05 12:47

> もう少し次回から抽象的な質問にします。 むしろ具体的な質問が歓迎されます。ただし大きなプログラムの全体を質問するのではなく、プログラムの中で自分が悩んでいる特定の部分のみに着目した小さくて具体的な質問にするのです。本件であれば、最初にN組の個体(=巡回ルート)を生成する必要がありますが、「これこれの構造にしたいのだが、乱数を使ってどうやってその構造を生成すればいいだろう?」と悩んだら、まずはそのことだけを質問します。プログラム全体となると回答しにくくても小さな部分問題なら回答がつく可能性は非常に大きくなると思いますよ。
okazujet

2017/03/05 13:03

>KSwordOfHasteさん ありがとうございます。 では質問を作り直したほうがよろしいでしょうか?
KSwordOfHaste

2017/03/05 13:41

> 質問を作り直した方がよい? ちょっとまよいますが、別途質問を起こし本質問は残しておいてもよいと思います。新たに起こした部分問題の質問の説明上「なぜこの質問をしているかの背景がわかるように」オリジナルの質問へのリンクが提示できたほうが都合がよいことがあるかも知れないからです。
KSwordOfHaste

2017/03/05 15:01

残念ながら「小さな部分問題を問う」質問にはなっていません。 自分の意図を再度申し上げると「ある程度自分で作成すること」およびそれを作る過程で「つまった点だけ(例えば特定の一つの関数や処理の実装についてのみ)質問する」ことです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問