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

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

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

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

Q&A

3回答

2993閲覧

ハノイの塔アルゴリズム

jgrialgj

総合スコア8

Ruby

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

1グッド

2クリップ

投稿2019/05/29 22:27

編集2019/05/31 02:50

def hanoi(n, from, to, via)
if n == 1
puts "#{from} から #{to} へ移す"
else
hanoi(n - 1, from, via, to)
puts "#{from} から #{to} へ移す"
hanoi(n - 1, via, to, from)
end
end

hanoi(3, :A, :B, :C)

この再帰プログラムの処理過程を誰か教えてくれませんか?

結果。円盤が 3枚の場合だと

A から B へ移す
A から C へ移す
B から C へ移す
A から B へ移す
C から A へ移す
C から B へ移す
A から B へ移す

こうです
引数の順序がコロコロ変わるんで頭が混乱してます

追記
わかりやすい回答ありがとございます。
ハノイの塔はアニメーション見てるので理解してます。
私が混乱してるのは引数の代入に関してです。
3枚だと仮定して7行目のhanoi(n - 1, via, to, from)でCにある二枚がBに移る必要があるのでこれには引数として
hanoi(n - 1, C, B, A)でいいと思うのですがこのメソッドの処理過程が真下に書いてないので混乱してます
まあおそらく1行目のメソッドであるdef hanoi(n, from, to, via)に戻るだけだと思うのですがこの場合は7行目のn - 1, C, B, Aを順番に引数と代入すれだけなのかなとは思いましたが

DrqYuto👍を押しています

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

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

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

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

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

y_waiwai

2019/05/29 22:44

質問は編集できますんで、新たに質問たてないで修正追記しましょう
asm

2019/06/02 04:56

過去質問含めて質問を〆ない態度では、今後回答が貰えなくなると思いますよ
guest

回答3

0

結果。円盤が 3枚の場合だと

A から B へ移す
A から C へ移す <-- 2)
B から C へ移す
A から B へ移す <-- 1) ここに注目
C から A へ移す
C から B へ移す <-- 3)
A から B へ移す

A から B にピラミッド3(n)枚を移す処理。真ん中の 1)に注目して、前半の処理と後半の処理に分けて考えます。

  • A から B へ移す <-- 1) ここに注目

これは3(n)枚目の最後の円盤を A から B へ移す処理。その時に、どうなっていなければならないか。

  • 前半の処理、A から経由する柱 C へ2(n-1)枚のピラミッドが移送されていなければならない。

大きな円盤の上に小さな円盤しか載せられないので2(n-1)枚のピラミッドができあがっている。
前半の移送処理の 2)をみればやはり2(n-1)枚目の最後の円盤の移送になっています。

  • 後半の処理、経由する柱 C から B へ2(n-1)枚のピラミッドを移動する。

3)をみればやはり2(n-1)枚目の最後の円盤の移送になっています。

原則

  • ピラミッドが1枚の時、直接移動する。(a->b)

a) 1枚を直接移動する。(a->b)

  • ピラミッドが2枚以上の時、移動は間接的に行う。(a->c,a->b,c->b)

a) まずピラミッドn-1枚を中間の柱に移動する。(a->c)
b) 最後の1枚を直接移動する。(a->b)
c) 中間の柱から目的の柱にピラミッドn-1枚を移動する。(c->b)

参考

『問題解決のPythonプログラミング ―数学パズルで鍛えるアルゴリズム的思考』 S. Devadas 2018 オライリージャパン
ハノイの塔の解法が載っているので立ち読みしてください。

『メタマジック・ゲーム―科学と芸術のジグソーパズル』 ダグラス・R. ホフスタッター 2005 白揚社
LISPを使うハノイの塔プログラムの紹介。円盤 3枚の解説も載っているので立ち読みしてください。

『コンピュータの数学』 ロナルド・L. グレアム、オーレン パタシュニク、ドナルド・E. クヌース 1993 共立出版
第1ページにハノイの塔の計算量の漸化式の解説があります。

追記

表示からわかること
中点にあたる移送を見れば、ピラミッドがどこに移動するのかが、わかります。

    1. n ピラミッドは、A->B へ移動
    1. n-1 ピラミッドは、A->C へ移動
    1. n-1 ピラミッドは、C->B へ移動

再帰の意味
再帰するのは、問題を小さな部分問題に分割して解くためです。再帰する都度、n, n-1, n-2, ... , 1 と問題が小さくなり、1 のとき再帰せずリターンします。ネストした再帰がすべてリターンすると、部分問題がすべて解かれ、問題全体が解かれたことになります。再帰の動作がわかりづらければシーケンス図を書きましょう。表でもよい。縦に処理の流れ、横に再帰のネストごとの処理を畫いてください。

追記(再帰呼出のトレース)

hanoi(n - 1, C, B, A)でいいと思うのですがこのメソッドの処理過程が真下に書いてないので混乱してます

コメントの内容から「ハノイの塔」の問題ではなく、それ以前の「再帰呼出し」が理解できていないと判断しました。トレースコードを示します。再帰を勉強してください。

再帰呼出トレース用コード
hanoi() の呼出/リターンを表示するための行を追加。

ruby

1def hanoi(n, from, to, via) 2 puts "hanoi(#{n}, #{from}, #{to}, #{via}) : enter" 3 if n == 1 4 puts "#{from} から #{to} へ移す" 5 else 6 hanoi(n - 1, from, via, to) 7 puts "#{from} から #{to} へ移す" 8 hanoi(n - 1, via, to, from) 9 end 10 puts "hanoi(#{n}, #{from}, #{to}, #{via}) : return" 11end 12 13hanoi(3, :A, :B, :C)

再帰呼出トレース
irbで実行した結果を編集しました。

irb

1hanoi(3, A, B, C) : enter 2 hanoi(2, A, C, B) : enter <-- 前半の移動 (A->C) 3 hanoi(1, A, B, C) : enter 4 A から B へ移す <-- この円盤は、この下で B->C に移動する 5 hanoi(1, A, B, C) : return 6 A から C へ移す <-- 2) 2枚目(前半(n-1)最後の円盤)の移動 7 hanoi(1, B, C, A) : enter 8 B から C へ移す 9 hanoi(1, B, C, A) : return 10 hanoi(2, A, C, B) : return 11 A から B へ移す <-- 1) 3枚目(全体(n)最後の円盤)の移動 12 hanoi(2, C, B, A) : enter <-- 後半の移動 (C->B) 13 hanoi(1, C, A, B) : enter 14 C から A へ移す <-- この円盤は、この下で A->B に移動する 15 hanoi(1, C, A, B) : return 16 C から B へ移す <-- 3) 2枚目(後半(n-1)最後の円盤)の移動 17 hanoi(1, A, B, C) : enter 18 A から B へ移す 19 hanoi(1, A, B, C) : return 20 hanoi(2, C, B, A) : return 21hanoi(3, A, B, C) : return 22=> nil

追記(再帰呼出の解説)

** 再帰呼出の動作**

再帰呼出しとはメソッド(hanoi)の中から自分自身(hanoi)を呼ぶことです。これは、自分自身を呼ぶことを除けば、普通のメソッド呼出しと同じです。メソッド呼出しの動作は以下のとおり。

  • def hanoiの(…)の括弧の中に記述されているのが引数です。n, from, to, viaです。
  • 引数に値を設定(代入)してメソッドに渡すと引数のコピーが作られます。

呼ばれたメソッドはコピーの値しか見えません。

  • 呼ばれたメソッドは引数の値を使って処理を実行します。

hanoi()では原則に従い n=1 かそれ以外 (n≠1) かで処理が異なります。

  • 処理が終わってメソッドからリターンする時に引数のコピーは消滅します。

メソッド呼び出しの都度、異なる引数の値がコピーされるため異なる動作ができるのです。

再帰呼出はループではありませんが、再帰呼出することはループの繰り返しと同じことをします。再帰呼出ししなければループ終了と同じ。hanoi()では n=1 の時再帰しません。

イメージ説明

投稿2019/05/30 10:49

編集2019/06/02 07:24
xebme

総合スコア1081

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

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

xebme

2019/05/30 19:43

ハノイの塔のアニメーションが公開されています。見て考えましょう。
xebme

2019/06/02 07:34

図はイメージです。実際の動作はプログラミングを深く学べばわかるようになります。
guest

0

xebme さんの説明で十分だとは思うのですが、puts が2度出てくるのが少し気になりました。

ruby

1def hanoi(n, from = "A", to = "B", via = "C") 2 return if n.zero? 3 hanoi(n - 1, from, via, to) 4 puts "#{from} => #{to}" 5 hanoi(n - 1, via, to, from) 6end 7 8hanoi(3)

こうすると、もう少し単純になって分かりやすくなるでしょうか。
余計な初期値まで設定してしまいました。

投稿2019/06/05 11:56

編集2019/06/05 12:02
kts_h

総合スコア207

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

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

0

プログラムを日本語にしてもわかりませんか?

塔ををAからBに、Cをワークに使って移動するためには、
まず、塔の一番下の円盤を除いた部分を、AからCに、Bをワークに使って移動し、
Aに残った一番下の円盤をBに異動して、
その後で、CからBに、Aをワークに使って移動する。

です。

投稿2019/05/30 15:06

otn

総合スコア84538

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問