ハノイの塔ソルバー


10

ハノイの塔とは何かを参照するには、グーグルで見るか、ウィキペディアのページをご覧ください。

あなたのコードは2つのことをすることができるはずであり、それらは次のとおりです:

  • ハノイタワーの始点にあるディスクの数を指定するユーザー入力を受け入れる
  • タワーパズルの解決策を示すために、(何らかの形で論理的である限り)選択した方法で出力を作成します。

論理出力の例は次のようになります(4ディスクの開始を使用)。

L1L2C1L1R-2R-1L1L2C1C-1R-2C1L1L2C1

Lは、左のペグ、C中央のペグR、右のペグを表し、数字は、そのペグ上でディスクをどれだけの距離、どの方向に動かすかを示します。正の数は、右端のペグに向かって移動するペグの数を表します(ディスクが左端のペグから始まるため)。

ハノイの塔へルールは簡単です:

  • 一度に移動できるディスクは1つだけです。
  • 各移動は、ペグの1つから上部ディスクを取り出し、それを別のペグに、そのペグに既に存在する可能性がある他のディスクの上にスライドさせることで構成されます。
  • 小さいディスクの上にディスクを配置することはできません。

ディスクは、左端のペグから始まり、一番下が最大、一番上が最小です。


任意の大きなタワーを解決する必要がありますか、それとも10、100、1k、1Mのディスクなど、想定できる制限はありますか?
ユーザー不明の

@userunknownなら、非常に大きな数についてはあまり心配しませんが、プログラムが処理できるディスクの最大数は、コンピューターのメモリ容量またはそのコールスタック制限(メモリはかなり一般的な用語なので、私が推測するのと同じようなものです。ただし、コードを送信する際に、任意の数を怖がらせないでください。あなたのソリューションが独創的であるが非常に多くのディスクしか処理できない場合でも、私はまだあなたに信用を与えます。
カーターペープ

まあ、私の考えはかなり非効率的な解法アルゴリズムでした、そしてもし限界があれば、プログラムが処理できれば、それで結構です。しかし、これまでの解決策を見て、まったく別のリーグでプレーすることに気づきました。
ユーザー不明の

回答:


2

ハスク、5バイト

↑≠⁰İr

オンラインでお試しください!出力の
それぞれnは、ディスクnを次に利用可能なペグに移動することを表します(循環的に折り返します)。

説明

   İr   The ruler sequence [0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, ...]
↑       Take while...
 ≠⁰     ... not equal to the input.

7

Python、76文字

def S(n,a,b):
 if n:S(n-1,a,6-a-b);print n,a,b;S(n-1,6-a-b,b)
S(input(),1,3)

たとえば、N = 3の場合は次を返します。

1 1 3  (move disk 1 from peg 1 to peg 3)
2 1 2  (move disk 2 from peg 1 to peg 2)
1 3 2  (move disk 1 from peg 3 to peg 2)
3 1 3  ...
1 2 1
2 2 3
1 1 3

私はゲームに少し遅れていることを知っていますが、これにより13文字が削られ
##

6

Perl-54文字

for(2..1<<<>){$_--;$x=$_&-$_;say(($_-$x)%3,($_+$x)%3)}

で実行しperl -M5.010、標準入力でディスクの数を入力します。

出力フォーマット:

移動ごとに1行、最初の桁はペグから、2桁目はペグ(0から開始)

例:

02 -- move from peg 0 to peg 2
01
21
02
10
12
02

ブレースを削除して5文字節約します。$x=--$_&-$_,say(($_-$x)%3,($_+$x)%3)for 2..1<<<>
マリナス

5

GolfScript(31 25 24文字)

])~{{~3%}%.{)3%}%2,@++}*

trのs /順列を6文字短縮できることを指摘してくれたIlmari Karonenに感謝します。それらを2つの順列の積として分解することで、もう1つ保存することができました。

3%長さを1 因数分解すると、次のようになります。

])~{{~}%.{)}%2,@++}*{3%}%

一部の人々は本当に複雑な出力フォーマットを持っています。これにより、(0、1、2の番号が付けられた)移動元のペグと移動先のペグが出力されます。仕様では、どのペグに移動するかは指定されていないため、ペグ1に移動します。

例えば

$ golfscript hanoi.gs <<<"3"
01021201202101

sedの同じロジックがさらに短いのは間違いありませんが、私のsedの能力はそれを下回っています。
Peter Taylor

1
あなたは25文字でそれを行うことができます:])~{.{3^3%}%2,@{2\-}%++}*
イルマリKaronen

3

Perl、75 79文字

キース・ランドールの出力フォーマットを完全に盗む:

sub h{my($n,$p,$q)=@_;h($n,$p^$q^h($n,$p,$p^$q),$q*say"@_")if$n--}h pop,1,3

呼び出し-M5.010のためにsay

(関数の戻り値を抑制するだけでなく、それを利用する方法を見つけることができれば、これは改善できると思います。)


[在庫「使用のみsay」の推奨]
JB

わかりました-しかし、文字数に対して5.10の機能を有効にするコストを含める必要はありませんか?
ブレッドボックス、

1
あなたはそうします— しかしそれは無料です。プログラムを呼び出す方法をメモしてください。そうすれば、perl呼び出しの詳細に堪能でない人でも、とにかくプログラムを試すことができます。
JB

リンクをありがとう; そんなことを先に探していました。
ブレッドボックス、2012年

3

SML、63

fun f(0,_,_)=[]|f(n,s,t)=f(n-1,s,6-s-t)@[n,s,t]@f(n-1,6-s-t,t);

次の関数f(n,s,t)を呼び出します:

  • n個のディスク
  • s開始点
  • tゴールポイント

2

バッシュ(64文字)

t(){ tr 2$1 $12 <<<$s;};for((i=$1;i--;))do s=`t 1`01`t 0`;done;t

GolfScriptの2倍の長さにもかかわらず、これを投稿するのは、の再利用がtとして機能するのが好きだからecho $sです。


2

Scala、92 88 87文字

def?(n:Int,a:Int,b:Int){if(n>0){?(n-1,a,a^b)
print(n,a,b);?(n-1,a^b,b)}};?(readInt,1,3)

出力フォーマット

disk = 3とすると、

(1,1,3)(2,1,2)(1,3,2)(3,1,3)(1,2,1)(2,2,3)(1,1,3) (disk number,from peg, to peg)
                                                   \---------------------------/       
                                                            Move 1              ... Move n

xorの素晴らしい使い方。
Peter Taylor

2

C、98 92 87文字

最も簡単なアルゴリズムを実装します。
出力は、ab ab ab各ペアが「一番上のディスクをペグaからペグbに移動する」ことを意味する形式です。
編集:移動は16進数でエンコードされるようになりました-0x12はペグ1からペグ2への移動を意味します。いくつかのキャラクターを保存しました。
EDIT:パラメータではなく標準入力から数値を読み取ります。より短い。
例:
% echo 3 | ./hanoi
13 12 32 13 21 23 13

n;
h(d){n--&&h(d^d%16*16,printf("%02x ",d,h(d^d/16))),n++;}
main(){scanf("%d",&n);h(19);}

誰かが関数h()の本体の構文を説明できますか?特にその再帰呼び出しでの明らかな2つの引数(d ^ d%16 * 16とprintf(...))、および最後の操作は明らかに最後までぶら下がっています。私の知識に基づいて、その関数には2つの構文エラーがありますが、ビルド(stdioを含む)して正しく実行されることはすでに知っています。
グリフィン

1
関数が必要とするよりも多くのパラメーターを渡すことが可能です。彼らの価値観はどこにも行きません。h(x,printf(...))が呼び出さprintfれる前に呼び出す方法hです。最後n++はインナーhが戻った後に作られます。最初のを元に戻すために使用されn--ます。
ugoren

ありがとう、それは理にかなっています(n ++の目的は明白でした)。カンマの代わりにn ++の前にセミコロンがないのはなぜですか、または違いがありますか?
グリフィン

@グリフィン、実際に;はここでも同じだろう。,はしばしば有用です(例えばをif(x)a,b;置き換えるif(x){a;b;})が、ここでは利点がありません。
ugoren

2

ゼリー、5バイト

2*Ṗọ2

オンラインでお試しください!

0最小のディスクを1スペース右に
1移動し(必要に応じて最初に折り返し)、2番目に小さいディスクを他の唯一の正当な列に
2移動します3番目に小さいディスクを他の唯一の正当な列に移動します

アルゴリズム

ハノイの塔問題の解決策を再帰的に見ることができます。サイズのスタックを移動させるために、NからAB、サイズのスタックを移動N -1からAC、サイズ、ディスクNからAB、次いでサイズのスタックを移動N -1からBC。これにより、次の形式のパターンが生成されます(このプログラムで使用される出力形式):

0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2 …

このシーケンスは、OEISではA007814であることがわかります。シーケンスのもう1つの可能な定義は、「シーケンスのk番目(1から始まる)の要素は、バイナリで記述した場合の番号kの末尾にあるゼロの数です」です。そして、それがここのプログラムが計算しているものです。

説明

まず、解の移動回数2 n -1 を計算します。実際に1つの余分な動きを計算して後で破棄するのが最も短いことがわかりました2*。つまり、これは、つまり2の何かの力です。(これまでに取った唯一の入力はコマンドライン引数なので、デフォルトで使用されます。)

次に、Jellyの組み込み関数を使用して、基数bの数値の末尾にあるゼロの数を計算します。それです。バイナリで計算しているので、です。必要なのは、この組み込みを1から2 n -1までの数値に適用することだけです。bọ2

そこゼリーに番号の範囲を反復処理するには、2つの簡単な方法があり、そしてR、この問題で私の以前の試みは、これらのいずれかを使用していました。ただし、この場合、少し短くすることができます。を入力として指定すると、1つの要素を停止する反復を実行できます(通常、1つの要素を除くすべての要素を処理するために使用される組み込み関数です)。これはまさにこの場合に必要なものです(2*生成される要素が1つ多すぎるため)。これを使用してリンクし2*ọ2に接続すると2*Ṗọ2、問題の5バイトの解決策が得られます。



1

Bashスクリプト、100 96文字

t(){ [[ $1<1 ]] && return
t $(($1-1)) $2 $(($2^$3))
echo $@
t $(($1-1)) $(($2^$3)) $3
}
t $1 1 3

出力フォーマットはKeith Randallのものと同じです。

1 1 3
2 1 2
1 3 2
3 1 3
1 2 1
2 2 3
1 1 3

編集peterのコメントにより4文字を保存しました。


1
スペースを追加して、エコーすることでいくつかの文字を節約できます$@
Peter Taylor

@PeterTaylor:いいですね。更新させてください。
ジョンウェスリー王子、

1

J、23バイト

2進数の解

2&(+/@:~:/\)@#:@i.@^~&2

このソリューションでは、このビデオで説明さているバイナリカウント方式を使用します。

つまり、1最大2桁の2進数を生成し、2^n長さ2のインフィックスを取得して、各ビットを前の数値の対応するビットと比較し、それらが等しくないかどうかを確認します。等しくないビットの数は、その移動の出力です。

出力、たとえば、3つのディスクの場合、最小のディスクには1のラベルが付けられます。

1 2 1 3 1 2 1

1 「最小のディスクを1ペグ右に移動し、必要に応じて最初のペグにループバックする」ことを意味します

n、その他すべてのn場合、「ディスクnを合法的なペグに移動する」ことを意味します(常に1つだけ存在します)

オンラインでお試しください!

再帰的ソリューション

((],[,])$:@<:)`]@.(1=])

上記のソリューションと同じ出力ですが、ここでのロジックにより、問題の再帰的な性質が明確になります。

それをツリーとして視覚化すると、この点も強調されます。

              4
             / \
            /   \
           /     \
          /       \
         /         \
        /           \
       /             \
      3               3      
     / \             / \    
    /   \           /   \
   /     \         /     \ 
  2       2       2       2  
 / \     / \     / \     / \
1   1   1   1   1   1   1   1

オンラインでお試しください!


1
元の質問が5時間以上前に提出されたこの質問への回答を確認するために戻ったのと同じ時間内に提起された後、5年以上あなたの回答を提出する偶然の性質…わあ。+1
Carter Pape 2017年



0

R、73バイト

Rを地図上に置く。[キースランドールの答え] [1]に触発されて入力が簡素化され、最後と最初のペグのみを印刷して2バイトを節約します。また、0インデックスのペグ。

f=function(n,s=0,e=2){if(n){f(n-1,s,3-s-e)
print(c(s,e))
f(n-1,3-s-e,e)}}

オンラインでお試しください!


0

JavaScript(ES6)、45b

h=(n,f,a,t)=>n?h(--n,f,t,a)+f+t+h(n,a,f,t):''

例:呼び出しh(4, 'A', 'B', 'C')(補助ペグBを使用して4つのディスクをペグAからペグCに移動)

戻り値'ABACBCABCACBABACBCBACABCABACBC'(ディスクをペグAからペグBに移動、ディスクをペグAからペグCに移動、ディスクをペグBからペグCに移動、など)


1
いいね。f、a、tパラメータに関数定義にデフォルトを含める必要があるかどうか疑問に思いますか?そうでなければ、提出は追加の引数に任意のデータを含めることができます。私は初心者ですから、もっと経験のある人にアドバイスしてください。
John Rees
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.