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

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

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

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

アルゴリズム

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

Q&A

解決済

2回答

738閲覧

A*アルゴリズムで壁を通る場合の計算になってしまう

minokiti

総合スコア45

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

アルゴリズム

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

0グッド

0クリップ

投稿2020/03/02 00:09

編集2020/03/02 00:37

RubyでA*アルゴリズムを使って最短経路を求めたい

RubyでA*アルゴリズムを書いているのですが、ゴールからの距離を求めるときに壁も通るようになってしまいます。

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

関数「distance」の定義の仕方のせいで壁(通らない座標)を通る場合の距離になってしまいます...

result

1:kaku_route 2[[1, 13], [1, 12], [1, 11], [1, 10], [2, 10], [2, 11], [3, 11], [4, 11], [4, 10]]

**最短経路が求まりません...

該当のソースコード

Ruby

1 2def distance(point1, point2) 3 return (point1[0] - point2[0]).abs + (point1[1] - point2[1]).abs 4end 5def dijkstra(s,g,e) 6 p_all_v = [] 7 d_hash = {} 8 before = [] 9 best_east = [] 10 best_west = [] 11 best_north = [] 12 best_south = [] 13 best_point = [] 14 directions = [] 15 kaku_route = [] 16 best_history = [] 17 test_route = [] 18 player_east = [s[0]+1,s[1]] 19 if !(e.include?(player_east)) 20 directions.push(player_east) 21 end 22 player_west = [s[0]-1,s[1]] 23 if !(e.include?(player_west)) 24 directions.push(player_west) 25 end 26 player_north = [s[0],s[1]-1] 27 if !(e.include?(player_north)) 28 directions.push(player_north) 29 end 30 player_south = [s[0],s[1]+1] 31 if !(e.include?(player_south)) 32 directions.push(player_south) 33 end 34 directions.each do |d| 35 p_distance = distance(s, d) 36 d_distance = distance(g, d) 37 before.push(p_distance + d_distance) 38 #後から最小のコストの座標を求めた時に座標を取り出さなければならないため、ハッシュで代入しておく 39 d_hash[d] = before[-1] 40 end 41 before.sort! 42 best = before[0] 43 best_point = d_hash.invert[best] 44 best_history.push(best_point) 45 kaku_route.push(best_point) 46 before = [] 47 d_hash = {} 48 directions = [] 49 #目的地を見つけるまで繰り返す 50 until (best_point == g) 51 best_east = [best_point[0]+1, best_point[1]] 52 if !(e.include?(best_east)) && !(best_east == s) && !(best_history.include?(best_east)) 53 directions.push(best_east) 54 end 55 best_west = [best_point[0]-1, best_point[1]] 56 if !(e.include?(best_west)) && !(best_west == s) && !(best_history.include?(best_west)) 57 directions.push(best_west) 58 end 59 best_north = [best_point[0], best_point[1]-1] 60 if !(e.include?(best_north)) && !(best_north == s) && !(best_history.include?(best_north)) 61 directions.push(best_north) 62 end 63 best_south =[best_point[0], best_point[1]+1] 64 if !(e.include?(best_south)) && !(best_south == s) && !(best_history.include?(best_south)) 65 directions.push(best_south) 66 end 67 directions.each do |d| 68 test_route = kaku_route 69 test_route.push(d) 70 p_distance = test_route.length 71 d_distance = distance(g, d) 72 before.push(p_distance + d_distance) 73 #後から最小のコストの座標を求めた時に座標を取り出さなければならないため、ハッシュで代入しておく 74 d_hash[d] = before[-1] 75 test_route.delete(d) 76 end 77 #昇順にソートする 78 before.sort! 79 #最小のコストのものを選ぶ 80 best = before[0] 81 #最小コストの座標 82 best_point = d_hash.invert[best] 83 best_history.push(best_point) 84 #最小のコストの座標を毎回配列に挿入する 85 kaku_route.push(best_point) 86 87 before = [] 88 directions = [] 89 d_hash = {} 90 end 91 kaku_route.unshift(s) 92 p :kaku_route,kaku_route 93end 94 95#通らない座標 96except = [[3,10],[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7],[0,8],[0,9],[0,10],[0,11],[0,12],[0,13], [0,14], [1,0],[2,0],[3,0],[4,0],[0,4],[5,0],[6,0],[7,0],[8,0],[9,0],[10,0],[11,0],[12,0],[13,0], [14,0]] 97dijkstra([1,13],[4,10],except)

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

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

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

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

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

guest

回答2

0

ベストアンサー

A*で使う推定距離は実際の距離より短ければなんでもいいので
壁を通った場合の距離でも問題ありません。

追記

実装してみた

ruby

1require 'set' 2 3class PriorityQueue 4 def initialize 5 @hash = Hash.new{|h,k| h[k] = [] } 6 end 7 def push(val, priority) 8 @hash[priority].push val 9 end 10 def min_key 11 @hash.select{|k,v| !v.empty?}.map(&:first).min 12 end 13 def pop 14 @hash[min_key].pop 15 end 16 def empty? 17 @hash.delete_if{|k,v| v.empty?} 18 .empty? 19 end 20end 21 22def distance(start, goal) 23 (start[0]-goal[0]).abs + (start[1]-goal[1]).abs 24end 25 26def a_star(start, goal, except) 27 directions = [[0,1],[0,-1],[1,0],[-1,0]] 28 que = PriorityQueue.new 29 que.push([start,[]], distance(start, goal)) 30 close = Set[] 31 until que.empty? 32 now, route = que.pop 33 next unless close.add?(now) 34 next_route = route + [now] 35 return next_route if now == goal 36 dist = next_route.size 37 dirs = directions.map{|dx,dy| [now[0] + dx, now[1] + dy] } 38 .select{|pos| !except.include?(pos) } 39 dirs.each do |to| 40 que.push([to,next_route], dist + distance(to, goal)) 41 end 42 end 43end 44 45except = [ 46 [3,10], # [4,11],[4,9],[5,10], 47 *Array.new(15){|i| [0,i] }, 48 *Array.new(15){|i| [i,0] }, 49 # *Array.new(14){|i| [14,i+1]}, 50 # *Array.new(14){|i| [i+1, 14]}, 51] 52p a_star([1,13],[4,10],except)

投稿2020/03/02 00:26

編集2020/03/03 00:00
asm

総合スコア15149

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

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

minokiti

2020/03/02 00:39

すみません。 説明の仕方が悪かったみたいです。 壁を通った場合の距離にすると最短経路が求まりません
asm

2020/03/02 06:09 編集

なるほど d_hashを毎回消してるせいでダイクストラ法としても成立してないのが問題のように思います
minokiti

2020/03/02 10:01

ありがとうございます! 無事動きました! ご迷惑をお掛けしました。初めての質問だったので不手際もあったかと思いますが、みなさんありがとうございました。
minokiti

2020/03/02 23:19

質問ですが、「require 'set'」って何をしているのですか?
asm

2020/03/02 23:46

既探索箇所を表す集合closeのためです。
minokiti

2020/03/20 13:11 編集

質問続きで申し訳ないですが、同じ座標(アイテム)の種類(複数の座標)をできるだけ通らないようにしたいのですが、どのようにすればよいのでしょうか...
asm

2020/03/20 09:03

「壁の種類」とは、なんでしょうか? 「できるだけ」とは具体的にできますか? もし、壁だけではなく段差みたいな通れるが固定のコストが掛かる場合は 今回省略していたコスト計算をしてやればどうにかなる場合はあります。 そうではなく扉と複数種類の鍵のような資源的制約がある場合はA*では難しいように思います。
minokiti

2020/03/20 13:12 編集

複数の座標をグループ化して通らない座標として固定はせずに通る回数をなるべく少なくする、みたいな感じです。
asm

2020/03/20 14:26 編集

グループ化とはどういう事でしょうか?複数グループあるのですか? もし、単一グループと見なしても変わらないのであれば、コストとしてマス数以上を計上してやればいいでしょう。 ただ、通りたく無い座標を通る度に全探索する羽目になるのでA*である必要が微妙ですね ダイクストラ法でも変わらないかもしれません。
minokiti

2020/03/20 22:06 編集

すみません。「グループ化」という表現が悪かったみたいです。特定の座標だけコストを上げることはできるのでしょうか?
asm

2020/03/21 07:12

それ自体は可能です。 が、提示したコードではその機能は省略していますのでちょっと厄介です。 省略せずにやるには、 > dist = next_route.size の部分を考え直す必要があります。 ここでは、過去の合計コストを計算しています。 いちいち計算してたら時間が掛かるので、queについでに保存してやる感じになるかと思います。
minokiti

2020/03/22 01:18 編集

最初に water = [[1,1],[3,6],[1,7],[4,7]] みたいな感じで定義しておいて if water.include(now)  dist = next_route.size + 1 else  dist = next_route.size end みたいな感じでいけませんか?
minokiti

2021/01/05 08:21 編集

すみません。やっぱり無理でした。queに保存するとは、具体的にどういうことか教えていただけいただけませんか?
guest

0

まず、葉っぱから
def distance(point1, point2) 冗長すぎて理解に時間がかかりました
(point1[0] - point2[0]).abs + (point1[1] - point2[1]).abs

次に幹
A* ですから ノード間のつながりの情報が必要です。
定義してあるexceptはノードの位置情報ですよね?
どのノードとどのノードがつながっているか、という情報がないです。
プログラム読んでませんが、多分全ノードが繋がり合ってるということになってませんか?

投稿2020/03/02 00:24

winterboum

総合スコア23567

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問