「プログラマ脳を鍛える数学パズル」という本に載っている
「Q59 ハンカチ落としの総走行距離」の解答コードで腑に落ちない部分があるので
どなたか教えていただけないでしょうか?
問題は以下です。
長文のためキャプチャ画像である事ご容赦下さい。
解答のコードは以下です
Ruby
1@n = 8 2# 鬼を含んだ配列。 3@all = (0..@n).to_a 4 5#開始時の位置は1を固定した1通り。終了は1を順にずらしたモノ全て。 6 7# 開始時の配置 8start = {} 9start[(1..@n).to_a] = [] 10 11# 終了時の配置 12goal = {} 13@n.times{|i| 14 goal[(1..@n).to_a.reverse.rotate(i)] = [] 15} 16 17# 移動距離を求める 18def dist(log) #log:ハンカチを落とした位置の記録 19 return 0 if log.size == 0 20 check = log.clone 21 pre = check.shift 22 sum = @n + pre 23 check.each{|c| 24 # %@n することにより0をまたぐ場合とそうでない場合をまとめて書ける 25 sum += @n + (c + @n - pre) % @n 26 pre = c 27 } 28 sum 29end 30 31# 探索(directionがtrueのとき順方向、falseのとき逆方向) 32def search(child, direction) 33 child.clone.each{|key, value| 34 oni = (@all - key)[0] # 使われていないのが鬼 35 @n.times{|i| #全ての位置に鬼を座らせる 36 k = key.clone 37 k[i] = oni #新たに鬼が座った配置 38 v = value + [i] #ハンカチを落とした位置のログ 39 if child.has_key?(k) then 40 if direction then # 順方向 41 child[k] = v if dist(v) < dist(child[k]) 42 else # 逆方向 43 child[k] = v if dist(v.reverse) < dist(child[k].reverse) 44 end 45 else 46 child[k] = v 47 end 48 } 49 } 50end 51 52cnt = 0 53while (start.keys & goal.keys).size == 0 do 54 if cnt % 2 == 0 then # 偶数のときは順方向に探索 55 search(start, cnt % 2 == 0) 56 else # 奇数のときは逆方向に探索 57 search(goal, cnt % 2 == 0) 58 end 59 cnt += 1 60end 61 62# 両方向からの探索結果が合流した場合、最短の移動距離を算出 63min = 98 64(start.keys & goal.keys).each{|c| 65 d = dist(start[c] + goal[c].reverse) 66 min = [min, d].min 67} 68 69puts min 70
幅優先探索でスタートとゴールから双方向探索して経路が重なるモノの中から最短になるモノを求めていることは
理解できました。
腑に落ちないのはsearch関数内の逆方向における次の部分です。
Ruby
1child[k] = v if dist(v.reverse) < dist(child[k].reverse)
ここで用いられているdist関数はスタート位置が0である設定で書かれているのですが、
実際に順方向から辿ってきた場合、逆方向のスタート位置は0とは限らず、
1から7のいずれかである場合もあります。
例えば0からスタートの場合
dist([0]) < dist([2])
ですが1からスタートの場合
dist([0]) > dist([2])
と大小関係が逆転し、距離に誤差が出ると思います。
その為に解答コードの最後の
Ruby
1(start.keys & goal.keys).each{|c| 2 d = dist(start[c] + goal[c].reverse) 3 min = [min, d].min 4}
では、
Ruby
1d = dist(start[c]) + dist(goal[c].reverse)
とせずに
Ruby
1d = dist(start[c] + goal[c].reverse)
と、ハンカチの落とした位置をスタートからゴールにまで新たに繋げ直して
dist関数に入れて正確な距離を出しているのだと思います。
なぜ
Ruby
1child[k] = v if dist(v.reverse) < dist(child[k].reverse)
では誤差を無視することが出来るのでしょうか?
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2020/01/17 13:50 編集
2020/01/18 07:14 編集