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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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

Q&A

0回答

472閲覧

sort()のkeyにコルーチンを指定したい

old.exe

総合スコア23

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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

0グッド

2クリップ

投稿2022/10/05 06:29

編集2022/10/05 08:04

前提

実行時間の短縮を目指して、遺伝的アルゴリズムで巡回セールスマン問題を解くプログラムに非同期処理を追加しました。このとき、経路を計算する関数calc_distanceをコルーチンにしたところ、sort(key=calc_distance)のところでエラーが出ました。最短経路をsortでリストの先頭に持っていき、先頭を別のリストに保存する方法をとっているのでsortが出来ないと困ってしまいます。

実現したいこと

・sort(key=)にコルーチンであるcalc_distanceを指定したい

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

33356.142646205066 Traceback (most recent call last): line 230, in <module> asyncio.run(main()) line 44, in run return loop.run_until_complete(main) line 642, in run_until_complete return future.result() line 207, in main population.sort(key= await Route.calc_distance) TypeError: object function can't be used in 'await' expression

ソースコード

python

1import asyncio 2import random 3import math 4import csv 5import copy 6import time 7 8cities_data = read_tspfile() 9population = [] # [[経路],[経路],[経路]...[経路]] 10cities = [] # Cityオブジェクトを入れるリスト 11CITIES_N = len(cities_data) # 都市数 12 13 14 15class City: 16 def __init__(self,num,X,Y): 17 self.num = num 18 self.X = X 19 self.Y = Y 20 21 22 23class Route: 24 def __init__(self): 25 self.distance = 0 26 27 # 経路を作成(重複なしのランダム) 28 self.citynums = random.sample(list(range(CITIES_N)),CITIES_N) 29 30 31 async def calc_distance(self): 32 """ citynumsリストの各都市間の距離の総和を求める """ 33 self.distance = 0 34 for i,num in enumerate(self.citynums): 35 """ 36 1つ前の都市との距離を計算 37 i=0のとき、i-1は最後の都市(最後の都市からスタートへの距離) 38 """ 39 self.distance += math.dist((cities[num].X, 40 cities[num].Y), 41 (cities[self.citynums[i-1]].X, 42 cities[self.citynums[i-1]].Y)) 43 return self.distance 44 45 46async def give2calc_dist(Route_obj): 47 return await Route_obj.calc_distance() 48 49 50async def copy_route(route): 51 return copy.deepcopy(route) 52 53 54async def mutate(c1,c2): 55 if random.random() > 0.5: 56 select_num = [i for i in range(len(c1.citynums))] 57 select_index = random.sample(select_num, 2) 58 59 a = c1.citynums[select_index[0]] 60 b = c1.citynums[select_index[1]] 61 c1.citynums[select_index[1]] = a 62 c1.citynums[select_index[0]] = b 63 64 else: 65 select_num = [i for i in range(len(c2.citynums))] 66 select_index = random.sample(select_num, 2) 67 68 a = c2.citynums[select_index[0]] 69 b = c2.citynums[select_index[1]] 70 c2.citynums[select_index[1]] = a 71 c2.citynums[select_index[0]] = b 72 73 return c1,c2 74 75 76 77async def crossover(p1, p2): 78 # 子の遺伝子情報 79 c1 = await copy_route(p1) 80 c2 = await copy_route(p2) 81 # 切り離す位置をランダムに選択 82 index = random.randint(1,len(cities_data)-2) 83 # indexの前までは自身の経路 84 #indexの後からは相方のリスト(index前の都市と重複しないように) 85 fragment_c1 = c1.citynums[:index] 86 fragment_c2 = c2.citynums[:index] 87 88 notinslice_c1 = [X for X in fragment_c2 if X not in c1.citynums] 89 notinslice_c2 = [X for X in fragment_c1 if X not in c2.citynums] 90 #リストを合体 91 c1.citynums += notinslice_c1 92 c2.citynums += notinslice_c2 93 94 mutated_c1, mutated_c2 = await mutate(c1,c2) 95 96 return mutated_c1,mutated_c2 97 98 99async def pfga(): 100 101 # 2未満なら追加。これだけだとランダムに2こ取り出す動作でエラー吐く。別途初期集団は作っておく 102 if len(population) < 2: 103 population.append(Route()) 104 105 # ランダムに2個取り出す 106 p1 = population.pop(random.randint(0, len(population)-1)) 107 p2 = population.pop(random.randint(0, len(population)-1)) 108 109 # 子を作成 110 c1, c2 = await crossover(p1,p2) 111 112 if await p1.calc_distance() < await p2.calc_distance(): 113 p_good = p1 # 短い経路(優秀) 114 p_bad = p2 # 長い経路(淘汰される) 115 else: 116 p_good = p2 117 p_bad = p1 118 if await c1.calc_distance() < await c2.calc_distance(): 119 c_good = c1 120 c_bad = c2 121 else: 122 c_good = c2 123 c_bad = c1 124 125 if await c_bad.calc_distance() <= await p_good.calc_distance(): 126 # 子2個体がともに親の2個体より良かった場合 127 # 子2個体及び適応度の良かった方の親個体計3個体が局所集団に戻り、局所集団数は1増加する。 128 population.append(c1) 129 population.append(c2) 130 population.append(p_good) 131 elif await p_bad.calc_distance() <= await c_good.calc_distance(): 132 # 子2個体がともに親の2個体より悪かった場合 133 # 親2個体のうち良かった方のみが局所集団に戻り、局所集団数は1減少する。 134 population.append(p_good) 135 elif await p_good.calc_distance() <= await c_good.calc_distance() and await p_bad.calc_distance() >= await c_good.calc_distance(): 136 # 親2個体のうちどちらか一方のみが子2個体より良かった場合 137 # 親2個体のうち良かった方と子2個体のうち良かった方が局所集団に戻り、局所集団数は変化しない。 138 population.append(c_good) 139 population.append(p_good) 140 elif await c_good.calc_distance() <= await p_good.calc_distance() and await c_bad.calc_distance() >= await p_good.calc_distance(): 141 # 子2個体のうちどちらか一方のみが親2個体より良かった場合 142 # 子2個体のうち良かった方のみが局所集団に戻り、全探索空間からランダムに1個体選んで局所集団に追加する。局所集団数は変化しない。 143 population.append(c_good) 144 population.append(Route()) 145 else: 146 raise ValueError("not comming") 147 148 149async def main(generation=500): 150 # citiesに読み込んだ座標を持つCityオブジェクトを入れる 151 for i in range(CITIES_N): 152 cities.append(City(cities_data[i][0], 153 cities_data[i][1], 154 cities_data[i][2])) # num,X,Yの順 155 156 # populationに個体を追加 157 for i in range(2): 158 population.append(Route()) 159 160 best = random.choice(population) # 個体(経路) 161 record_distance = await best.calc_distance() # 距離 162 163 with open('asyncio_PfGA_result.csv','w') as fout: 164 165 csvout = csv.writer(fout) 166 result = [] 167 168 for i in range(generation): 169 print(record_distance) 170 population.sort(key= await Route.calc_distance) 171 distance1 = await population[0].calc_distance() # 最短経路 172 173 if distance1 < record_distance: 174 record_distance = distance1 175 best = population[0] # 最短経路を更新 176 177 task1 = asyncio.create_task(pfga()) 178 #task2 = asyncio.create_task(pfga()) 179 180 await task1 181 #await task2 182 183 if generation == 1 or generation%100 == 0: 184 data = [] 185 data.extend([record_distance]) 186 result.append(data) 187 if i == generation: 188 csvout.writerows(result) 189 print(best.citynums) 190 191 192start = time.time() 193asyncio.run(main()) 194end = time.time() 195print(end-start)

試したこと

今のところ、sortのkeyにコルーチンを指定する方法が分からないので、ここに記載するのは変かもしれませんが、経路を計算するcalc_distance自体はコルーチンにしないで、新たに以下のような関数を作りました。

python

1async def give2calc_dist(Route_obj): 2 return Route_obj.calc_distance()

calc_distanceの部分を全てこれに置き換えると、以下のコードになります。

python

1async def pfga(): 2 3 # 2未満なら追加。これだけだとランダムに2こ取り出す動作でエラー吐く。別途初期集団は作っておく 4 if len(population) < 2: 5 population.append(Route()) 6 7 # ランダムに2個取り出す 8 p1 = population.pop(random.randint(0, len(population)-1)) 9 p2 = population.pop(random.randint(0, len(population)-1)) 10 11 # 子を作成 12 c1, c2 = await crossover(p1,p2) 13 14 if await give2calc_dist(p1) < await give2calc_dist(p2): 15 p_good = p1 # 短い経路(優秀) 16 p_bad = p2 # 長い経路(淘汰される) 17 else: 18 p_good = p2 19 p_bad = p1 20 if await give2calc_dist(c1) < await give2calc_dist(c2): 21 c_good = c1 22 c_bad = c2 23 else: 24 c_good = c2 25 c_bad = c1 26 27 if await give2calc_dist(c_bad) <= await give2calc_dist(p_good): 28 # 子2個体がともに親の2個体より良かった場合 29 # 子2個体及び適応度の良かった方の親個体計3個体が局所集団に戻り、局所集団数は1増加する。 30 population.append(c1) 31 population.append(c2) 32 population.append(p_good) 33 elif await give2calc_dist(p_bad) <= await give2calc_dist(c_good): 34 # 子2個体がともに親の2個体より悪かった場合 35 # 親2個体のうち良かった方のみが局所集団に戻り、局所集団数は1減少する。 36 population.append(p_good) 37 elif await give2calc_dist(p_good) <= await give2calc_dist(c_good) and await give2calc_dist(p_bad) >= await give2calc_dist(c_good): 38 # 親2個体のうちどちらか一方のみが子2個体より良かった場合 39 # 親2個体のうち良かった方と子2個体のうち良かった方が局所集団に戻り、局所集団数は変化しない。 40 population.append(c_good) 41 population.append(p_good) 42 elif await give2calc_dist(c_good) <= await give2calc_dist(p_good) and await give2calc_dist(c_bad) >= await give2calc_dist(p_good): 43 # 子2個体のうちどちらか一方のみが親2個体より良かった場合 44 # 子2個体のうち良かった方のみが局所集団に戻り、全探索空間からランダムに1個体選んで局所集団に追加する。局所集団数は変化しない。 45 population.append(c_good) 46 population.append(Route()) 47 else: 48 raise ValueError("not comming") 49 50 51async def main(generation=500): 52 # citiesに読み込んだ座標を持つCityオブジェクトを入れる 53 for i in range(CITIES_N): 54 cities.append(City(cities_data[i][0], 55 cities_data[i][1], 56 cities_data[i][2])) # num,X,Yの順 57 58 # populationに個体を追加 59 for i in range(2): 60 population.append(Route()) 61 62 best = random.choice(population) # 個体(経路) 63 record_distance = await give2calc_dist(best) # 距離 64 65 with open('asyncio_PfGA_result.csv','w') as fout: 66 67 csvout = csv.writer(fout) 68 result = [] 69 70 for i in range(generation): 71 print(record_distance) 72 population.sort(key=Route.calc_distance) 73 distance1 = await give2calc_dist(population[0]) # 最短経路 74 75 if distance1 < record_distance: 76 record_distance = distance1 77 best = population[0] # 最短経路を更新 78 79 task1 = asyncio.create_task(pfga()) 80 #task2 = asyncio.create_task(pfga()) 81 82 await task1 83 #await task2 84 85 if generation == 1 or generation%100 == 0: 86 data = [] 87 data.extend([record_distance]) 88 result.append(data) 89 if i == generation: 90 csvout.writerows(result) 91 print(best.citynums)

こうすると一応正常に動きました。しかし、計算処理自体はおそらく非同期になってないです。

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

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

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

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

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

quickquip

2022/10/05 07:14

質問が書かれていないようです
bsdfan

2022/10/05 08:54

一つ前の質問の回答にあるように、これは asyncio で実行時間の短縮ができるようなケースではないと思うのですが。
old.exe

2022/10/05 10:13

ありがとうございます。Multiprocessingを使ってみます。
quickquip

2022/10/05 11:10

(マルチプロセスでソートする実装はどこから見つけてくるんでしょう。そして速くなるんでしょうか?) コルーチンでも同じなんですが、どこがどういう風に「実行時間の短縮を目指して」につながるのかがわからないという点で引っかかって、何を答えるといいのかわかりません
old.exe

2022/10/05 12:03

コメントありがとうございます。混乱を招くような質問をしてしまいすみません。経路を計算するcalc_distanceを並列処理または非同期処理出来れば時短に繋がると思い試行錯誤している中での質問でした。問題を明確にした上で改めて質問させていただこうと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

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

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

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問