ハノイの塔ソリューションを検証する


29

ハノイ塔が何であるかわからない場合は、簡単に説明します。3本のロッドと、サイズの異なるディスクがいくつかあります。最初は、すべてのディスクが最初のタワーに並べられています。最大のものは一番下にあり、一番小さいものは一番上にあります。目標は、すべてのディスクを3番目のロッドに持ち込むことです。簡単に聞こえますか?キャッチは次のとおりです。他のディスクより小さいディスクの上にディスクを配置することはできません。一度に1枚のディスクを手に持って別のロッドに移動することができ、ディスクをロッドに置くことはできますが、テーブルではなく、卑劣な野郎です。

asciiのサンプルソリューション:

  A      B      C
  |      |      |      
 _|_     |      |      
__|__    |      |


  A      B      C
  |      |      |      
  |      |      |      
__|__   _|_     |


  A      B      C
  |      |      |      
  |      |      |      
  |     _|_   __|__


  A      B      C
  |      |      |      
  |      |     _|_     
  |      |    __|__      

チャレンジ

A、B、Cと呼ばれる3本のロッドがあります(それが役立つ場合は、それぞれ1、2、3と呼ぶこともできます)最初は、n個のディスクすべてがロッドA(1)にあります。

あなたの課題は、ハノイの塔の解決策を検証することです。以下を確認する必要があります。

  1. 最終的に、n個のディスクはすべてロッドC(3)にあります。
  2. 特定の状態の任意のディスクについて、その下に小さなディスクはありません。
  3. 空のロッドからディスクを取り出そうとしたり、ディスクを存在しないロッドに移動したりするような明らかなエラーはありません。

(ソリューションは最適である必要はありません。)

入力

プログラムは2つの入力を受け取ります。

  1. ディスクの数n(整数)
  2. 次のタプルのセットで構成される移動:(現在最上位のディスクを取得するタワー)、(このディスクを取得するタワー)各タプルは移動を指します。それらの表示方法を選択できます。たとえば、上記のasciiで描いたn = 2の解を表す次のような方法です。(テストケースでは最初のものを使用します。見た目が簡単だからです):

    「A-> B; A-> C; B-> C」

    [( "A"、 "B")、( "A"、 "C")、( "B"、 "C")]

    [(1,2)、(1,3)、(2,3)]

    「ABACBC」

    [1,2,1,3,2,3]

出力

  • 「チャレンジ」の下で見つけることができる条件が保持される場合、真実。

  • 偽物、そうでない場合。

テストケース:

正しい:

n=1, "A->C"

n=1, "A->B ; B->C"

n=2, "A->B ; A->C ; B->C"

n=2, "A->C ; C->B ; A->C ; B->C"

n=2, "A->C ; A->B ; C->B ; B->A ; B->C ; A->C"

n=3, "A->C ; A->B ; C->B ; A->C ; B->A ; B->C ; A->C"

n=4, "A->B ; A->C ; B->C ; A->B ; C->A ; C->B ; A->B ; A->C ; B->C ; B->A ; C->A ; B->C ; A->B ; A->C ; B->C"

偽:

@MartinEnderが提案した3番目、@ Joffanが7番目

n=1, "A->B"

n=1, "C->A"

n=2, "A->C ; A->B ; C->B ; A->C ; B->A ; B->C ; A->C"

n=2, "A->B ; A->C ; C->B"

n=2, "A->C ; A->B ; C->B ; B->A"

n=2, "A->C ; A->C"

n=3, "A->B ; A->D; A->C ; D->C ; A->C"

n=3, "A->C ; A->C ; A->B ; C->B ; A->C ; B->A ; B->C ; A->C"

n=3, "A->C ; A->B ; C->B ; A->B ; B->C ; B->A ; B->C ; A->C"

n=3, "A->C ; A->B ; C->B ; A->C ; B->A ; B->C ; C->B"

n=4, "A->B ; A->C ; B->C ; A->B ; C->A ; C->B ; A->B ; A->C ; B->C ; B->A ; C->A ; B->C ; A->B ; A->C"

n=4, "A->B ; A->B ; A->B ; A->C ; B->C ; B->C ; B->C"

これはcode-golfであり、最短のソリューションが勝ちます。標準のルールと抜け穴が適用されます。電池は含まれていません。


第二の入力は、あなたの方法を用いて表現することができれば大丈夫もありますが、数字の代わりに文字(すなわち使用してA=1B=2C=3、など)?
R. Kap

1
入力のインデックスをゼロにできますか?
ローハンジュンジュンワラ

1
空または存在しないロッドからディスクを取り出したときにエラーがスローされても大丈夫ですか?
R. Kap

1
のような非動きはないと仮定できますA->Aか?
マーティンエンダー

2
@Kobiを確認する必要があるmoving discs to nonexistant rods.ので、もちろんそうD
です-edc65

回答:


7

網膜84 80バイト

マーティン・エンダーのおかげで-5バイト

~
 ~$'
$
ABC
{`^(.)(.*)( ~+)\1
$3$2$1
}`^(\W+)(\w)(.*)(?<=\1~+|\w)\2
$3$1$2
^AB 

オンラインでお試しください!(さらに、行ごとのテスト用に5バイト)

コードは完全なゲームをシミュレートします。

  • 入力はとして与えられますACABCBACBABCAC~~~
    ~~~3枚のディスクを意味します。
  • 最初の4行は、入力をゲーム形式に変換しますACABCBACBABCAC ~~~ ~~ ~ABC
    最初は、Aロッドには3枚すべてのディスクがあり、BおよびCロッドは空です。
  • 次に、2つのステップのループがあります。
    • 次のソースロッドを示す行の最初の文字を取ります。このロッドを見つけて、最後のディスクを入れます。文字を取り外して、ディスクをスタークに移動します(ピックアップします)。
      例では、最初のステップの後、テキストは次のようになります ~CABCBACBABCAC ~~~ ~~ABC
    • 第2段階では、ターゲットロッドを見つけ、そこにディスクを移動します。ロッドが空であること、または上部に大きなディスクがあることを確認しますABCBACBABCAC ~~~ ~~AB ~C
  • 最後に、AロッドとBロッドが空であることを確認します-これは、すべてのディスクがCにあることを意味します(最後の行に余分なスペースがあります)。

うわー、thatsの印象
ロハンJhunjhunwala

17

網膜167の 165 157 150 123バイト

これは完全に1つの正規表現で解決する必要がある課題のように見えます(「Retina」というヘッダーにもかかわらず、これは有効な入力に一致する単なる.NET正規表現です)。

^(?=\D*((?=(?<3>1+))1)+)((?=A(?<1-3>.+)|B(?<1-4>.+)|C(?<1-5>.+)).(?=A.*(?!\3)(\1)|B.*(?!\4)(\1)|C.*(?!\5)(\1)).)+(?!\3|\4)1

入力形式はフォームの指示のリストであり、数字を使用ABnて単項で続き1ます。区切り記号はありません。出力は1有効および0無効です。

オンラインでお試しください!(最初の2文字は、改行で区切られたテストスイートを有効にします。)

代替ソリューション、同じバイト数:

^(?=\D*((?=(?<3>1+))1)+)((?=A(?<1-3>.+)|B(?<1-4>.+)|C(?<1-5>.+)).(?=A.*(?!\3)(\1)|B.*(?!\4)(\1)|C.*(?!\5)(\1)).)+(?<-5>1)+$

これは、おそらく使用することによって短縮することができ111そして111代わりにABC私は後でそれを検討する必要があります。また、プログラムをいくつかの段階に分割する方が短いかもしれませんが、その課題はどこにありますか?;)

説明

このソリューションは、.NETのバランスグループを多用します。詳細な説明については、Stack Overflowに関する私の投稿を参照してください。ただし、要点は、.NETのキャプチャグループはスタックであり、新しいキャプチャごとに別のサブストリングをプッシュし、そのようなスタックから再びポップすることも可能です。これにより、文字列内のさまざまな数量をカウントできます。この場合、3つのロッドを3つの異なるキャプチャグループとして直接実装でき、各ディスクはキャプチャで表されます。

ロッド間でディスクを移動するには、(?<A-B>...)構文の奇妙な癖を利用します。通常、これはスタックからキャプチャをポップし、ポップされたキャプチャとこのグループの先頭の間の文字列BをスタックにプッシュしますA。したがって、(?<A>a).(?<B-A>c)matched against abcA空のままBb(withとは対照的にc)のままになります。ただし、.NETの可変長のルックビハインドにより、キャプチャ(?<A>...)(?<B-A>...)オーバーラップが可能です。何らかの理由で、その場合、2つのグループの共通部分はにプッシュされBます。この回答では、グループのバランスに関する「高度なセクション」でこの動作を詳しく説明しました。

正規表現について。ロッドABおよびCグループ34および5正規表現に対応します。ロッドを初期化することから始めましょうA

^                 # Ensure that we start at the beginning of the input.
(?=               # Lookahead so that we don't actually move the cursor.
  \D*             # Skip all the instructions by matching non-digit characters.
  (               # For each 1 at the end of the input...
    (?=(?<3>1+))  # ...push the remainder of the string (including that 1)
                  # onto stack 3.
  1)+
)

たとえば、入力がで終わる場合、111グループ3 / rod Aはキャプチャのリストを保持します[111, 11, 1](上部が右側)。

コードの次のビットの構造は次のとおりです。

(
  (?=A...|B...|C...).
  (?=A...|B...|C...).
)+

このループの各反復は、1つの命令を処理します。最初の交替は、指定されたロッドからディスクを(一時的なグループに)引き出し、2番目の交替はそのディスクを他の指定されたロッドに置きます。これがどのように機能し、移動が有効であることを確認する方法をすぐに確認します。

まず、ソースロッドからディスクを取り出します。

(?=
  A(?<1-3>.+)
|
  B(?<1-4>.+)
|
  C(?<1-5>.+)
)

これは、上で説明した奇妙なグループ交差動作を使用しています。そのグループに注意してください345いつものサブストリング保持する1長さのディスクのサイズに対応した文字列の末尾にSを。ここで(?<1-N>.+)、スタックNから一番上のディスクをポップし、この部分文字列と一致.+する部分をスタックにプッシュします1.+常に飛び出したキャプチャ全体を常にカバーするためN、これは単にキャプチャを移動することを知っています。

次に、このディスクをスタックから12番目のロッドに対応するスタックに配置します。

(?=
  A.*(?!\3)(\1)
|
  B.*(?!\4)(\1)
|
  C.*(?!\5)(\1)
)

スタックをクリーンアップする必要はないことに注意してください1、スタックを再び使用する前に新しいものを上に置くので、ディスクをそのままにしておくことができます。これは、(?<A-B>...)構文を回避し、単に文字列をでコピーできることを意味し(\1)ます。移動が有効であることを確認するには、負の先読みを使用し(?!\N)ます。これにより、現在のディスクと一致させたい位置から、すでにスタックにあるディスクと一致させることができなくなりますN。これは\N、a)スタックが完全に空であるために一致しない場合、またはb)the disc on top of stackN is larger than the one we're trying to match with\ 1`の場合にのみ発生します。

最後に、残っているすべては、我々はすべての命令およびb)棒をマッチしてきた)ことを保証しているAB、空になっているので、すべてのディスクが上に移動されていることC

(?!\3|\4)1

どちら\3\4一致しないことを確認するだけです(実際のディスク一致するため、両方が空の場合にのみ1当てはまります)。


14

Java "only" 311 272 263 261 260 259 256バイト

保存された39古いデバッグ機能に気付いによる@Froznに無数のバイトだけでなく、いくつかの巧妙なゴルフのトリック。

ゴルフバージョン

int i(int n,int[]m){int j=0,k=0,i=n;Stack<Integer>t,s[]=new Stack[3];for(;j<3;)s[j++]=new Stack();for(;i-->0;)s[0].push(i);for(;k<m.length;k+=2)if((t=s[m[k+1]]).size()>0&&s[m[k]].peek()>t.peek())return 0;else t.push(s[m[k]].pop());return s[2].size()<n?0:1;}

各ステップで説明ときれいに印刷されたスタックで手放せない

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package codegolf;

/**
 *
 * @author rohan
 */
import java.util.Arrays;
import java.util.Stack;
public class CodeGolf {
    //golfed version
    int i(int n,int[]m){int j=0,k=0,i=n;Stack<Integer>[] s=new Stack[3];for(;j<3;j++)s[j]=new Stack();for(;i-->0;)s[0].push(i);for(;k<m.length;System.out.println(Arrays.toString(s)),k+=2)if(!s[m[k+1]].isEmpty()&&s[m[k]].peek()>s[m[k+1]].peek())return 0;else s[m[k+1]].push(s[m[k]].pop());return s[2].size()==n?1:0;}
    /** Ungolfed
        * 0 as falsy 1 as truthy
        * @param n the number of disks
        * @param m represents the zero indexed stacks in the form of [from,to,from,to]
        * @return 0 or 1 if the puzzle got solved, bad moves result in an exception
        */
    int h(int n, int[] m) {
        //declarations
        int j = 0, k = 0, i = n;
        //create the poles
        Stack<Integer>[] s = new Stack[3];
        for (; j < 3; j++) {
            s[j] = new Stack();
        }
        //set up the first tower using the "downto operator
        for (; i-- > 0;) {
            s[0].push(i);
        }
    //go through and perform all the moves
        for (; k < m.length; System.out.println(Arrays.toString(s)), k += 2) {
            if (!s[m[k + 1]].isEmpty() && s[m[k]].peek() > s[m[k + 1]].peek()) {
                return 0;//bad move
            } else {
                s[m[k + 1]].push(s[m[k]].pop());
            }
        }
        return s[2].size() == n ? 1 : 0;// check if all the disks are done
    }
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
    //test case
        System.out.println( new CodeGolf().h(3,new int[]{0,2,0,1,2,1,0,2,1,0,1,2,0,2})==1?"Good!":"Bad!");
    }

}

未使用バージョンには、各ステップでスタックがどのように見えるかを出力する機能があります...

[[2, 1], [], [0]]
[[2], [1], [0]]
[[2], [1, 0], []]
[[], [1, 0], [2]]
[[0], [1], [2]]
[[0], [], [2, 1]]
[[], [], [2, 1, 0]]
Good!

何をSystem.out.println(Arrays.toString(s))するの?
Frozn

スタックをきれいに印刷します。同様に[[2,1,0]、[] []]
ローハンジュンジュンワラ

おっと、今取り外しデバッグ機能でした@Frozn
ロハンJhunjhunwala

私はちょうどそれがありますなぜ:)あなたも置き換えることができます思って、知っている&&&
Frozn

@Frozn空のスタックを覗こうとするのを避けるために短絡動作に頼っていたので、悲しいことにそれを置き換えることはできません。39バイトの削減をありがとう
Rohan Jhunjhunwala

9

パイソン2、186 167 158 135 127 115の 110 102バイト

n,m=input()
x=[range(n),[],[]]
for a,b in m:p=x[a].pop();e=x[b];e and 1/(p>e[-1]);e+=p,
if x[0]+x[1]:_

STDINで次の形式で入力を取得します。

(1,[(0,1),(1,2)])

つまり、ディスク数のPythonタプルとのタプルのPythonリストです(from_rod,to_rod)。Pythonと同様に、周囲の括弧はオプションです。ロッドのインデックスはゼロです。

たとえば、このテストケースは次のとおりです。

n=2; "A->B ; A->C ; B->C"

次のように与えられます:

(2,[(0,1),(0,2),(1,2)])

ソリューションが有効な場合、何も出力せず、終了コード0で終了します。無効な場合、例外をスローし、終了コード1で終了します。IndexError存在しないロッドに移動するか、ディスクを取り出そうとすると、ディスクが付いていないロッドZeroDivisionError、ディスクが小さいディスクの上に置かれているNameError場合、またはディスクが最後の1番目または2番目のロッドに残っている場合。

@KarlKastorのおかげで13バイト節約されました!

@xnorのおかげで8バイト節約されました!


1
各パイルがソートされていることのチェックは複雑すぎるようです。移動したディスクが、移動先のパイルの一番上のディスクよりも大きいことを確認できませんか?
-xnor

@xnorありがとう、それはうまくいくはずです。今すぐ追加します。

5

パイソン2.7、173 158 138 130 127 123バイト。

r=range;a,b=input();U=[r(a,0,-1),[],[]]
for K,J in b:U[J]+=[U[K].pop()]if U[J]<[1]or U[K]<U[J]else Y
print U[-1]==r(a,0,-1)

各ムーブに対応するタプルを含む配列として指定された形式(<Number of Discs>,<Moves>)で標準入力を介して入力を受け取ります。<Moves>各タプルにはコンマで区切られた整数のペアが含まれます。たとえば、テストケース:

n=3, "A->C ; A->B ; C->B ; A->C ; B->A ; B->C ; A->C" 

投稿で与えられたものは次のように与えられます:

(3,[(0,2),(0,1),(2,1),(0,2),(1,0),(1,2),(0,2)]) 

私のプログラムに。出力IndexError第三条件が満たされない場合、NameError第二の条件が満たされない場合、およびFalse第一の条件が満たされない場合。それ以外の場合は出力しますTrue


2つのこと:変数Yはコード内で決して定義されず(Jである必要があると思います)、U[J]+=[Y,[U[K].pop()]][U[J]<[1]or U[K]<U[J]]3文字短くなりますstmt1 if cond else stmt2
-jermenkoo

@jermenkooまあ、私はそのYような変数を使用してNameError、2番目の条件が満たされないときはいつでもを上げる。私がに変更Yする場合J、それNameErrorは発生しません。このため、2番目の条件が満たされていないときだけでなくU[J]+=[Y,[U[K].pop()]][U[J]<[1]or U[K]<U[J]]これがNameError 常に発生するため、私もできません。
R. Kap

わかりました、あなたの説明をありがとう!
jermenkoo

5

VBA、234の 217 213 196バイト

Function H(N,S)
ReDim A(N)
While P<Len(S)
P=P+2:F=1*Mid(S,P-1,1):T=1*Mid(S,P,1)
E=E+(T>2):L=L+T-F
For i=1 To N
If A(i)=F Then A(i)=T:Exit For
E=E+(A(i)=T)+(i=N)
Next
Wend
H=L+9*E=2*N
End Function

移動の入力形式は、偶数桁(012)の文字列です。呼び出しはスプレッドシートにあり、= H([ディスク数]、[文字列を移動])

配列Aは、さまざまなディスクのロッド位置を保持します。移動とは、最初に出現した「From」ロッド番号を「To」ロッド番号に更新することです。最初に「To」ロッドディスクに遭遇するか、「From」ロッドディスクに遭遇しない場合、それは無効な動きです。Aの合計「ロッド値」はLに保持され、2Nで終了する必要があります。エラーは、Eの負の数として累積されます。

他のソリューションと同様に、ディスクをタワーから同じタワーに「移動」することは禁止されていません。さらに6バイトは禁止します。

結果

最初の列の関数の結果(最後のn = 3の場合は、追加のロッドを使用した追加です)。

TRUE    1   02
TRUE    1   0112
TRUE    2   010212
TRUE    2   02210212
TRUE    2   020121101202
TRUE    3   02012102101202
TRUE    4   010212012021010212102012010212

FALSE   1   01
FALSE   1   20
FALSE   2   02012102101202
FALSE   2   010221
FALSE   2   02012110
FALSE   2   0202
FALSE   3   0202012102101202
FALSE   3   0201210112101202
FALSE   3   02012102101221
FALSE   3   0103023212
FALSE   4   0102120120210102121020120102
FALSE   4   01010102121212

2

php、141バイト

<?php $a=$argv;for($t=[$f=range($a[++$i],1),[],[]];($r=array_pop($t[$a[++$i]]))&&$r<(end($t[$a[++$i]])?:$r+1);)$t[$a[$i]][]=$r;echo$t[2]==$f;

コマンドラインスクリプトは、入力を高さとして、次に1または2の高さの最短テストケースの場合、1 0 2または2 0 1 0 2 1 2などの一連の配列インデックス(0インデックス)を受け取ります。
真の場合はエコー1、偽の場合はエコーしません。
2通の通知と1通の警告を出すので、それらを黙らせる環境で実行する必要があります。


1

JavaScript(ES6)、108

n=>s=>!s.some(([x,y])=>s[y][s[y].push(v=s[x].pop())-2]<v|!v,s=[[...Array(s=n)].map(_=>s--),[],[]])&s[2][n-1]

入力形式: 2つの引数を持つ関数

  • arg 1、数値、リングの数
  • arg 2、文字列の配列、各文字列2文字 '0'、 '1'、 '2'

出力:正常な場合は1、無効な場合は0、ロッドが存在しない場合は例外を返します

少ないゴルフと説明

n=>a=>(
  // rods status, rod 0 full with an array n..1, rod 1 & 2 empty arrays
  s = [ [...Array(t=n)].map(_=>t--), [], [] ],
  // for each step in solution, evaluate function and stop if returns true
  err = a.some( ([x,y]) => {
    v = s[x].pop(); // pull disc from source rod
    // exception is s[x] is not defined
    if (!v) return 1; // error source rod is empty
    l = s[y].push(v); // push disc on dest rod, get number of discs in l
    // exception is s[y] is not defined
    if(s[y][l-2] < v) return 1; // error if undelying disc is smaller
  }),
  err ? 0 // return 0 if invalid move
  : s[2][n-1]; // il all moves valid, ok if the rod 2 has all the discs
)

テスト ノート:テスト関数の最初の行は、質問で与えられた入力形式を私の関数が期待する入力に変換するために必要です

F=
n=>s=>!s.some(([x,y])=>s[y][s[y].push(v=s[x].pop())-2]<v|!v,s=[[...Array(s=n)].map(_=>s--),[],[]])&s[2][n-1]

Out=x=>O.textContent+=x+'\n'

Test=s=>s.split`\n`.map(r=>[+(r=r.match(/\d+|.->./g)).shift(),r.map(x=>(parseInt(x[0],36)-10)+''+(parseInt(x[3],36)-10))])
.forEach(([n,s],i)=>{
  var r
  try {
    r = F(+n)(s);
  } 
  catch (e) {
    r = 'Error invalid rod';
  }
  Out(++i+' n:'+n+' '+s+' -> '+r)
})

Out('OK')
Test(`n=1, "A->C"
n=1, "A->B ; B->C"
n=2, "A->B ; A->C ; B->C"
n=2, "A->C ; C->B ; A->C ; B->C"
n=2, "A->C ; A->B ; C->B ; B->A ; B->C ; A->C"
n=3, "A->C ; A->B ; C->B ; A->C ; B->A ; B->C ; A->C"
n=4, "A->B ; A->C ; B->C ; A->B ; C->A ; C->B ; A->B ; A->C ; B->C ; B->A ; C->A ; B->C ; A->B ; A->C ; B->C"`)

Out('\nFail')
Test( `n=1, "A->B"
n=1, "C->A"
n=2, "A->C ; A->B ; C->B ; A->C ; B->A ; B->C ; A->C"
n=2, "A->B ; A->C ; C->B"
n=2, "A->C ; A->B ; C->B ; B->A"
n=2, "A->C ; A->C"
n=3, "A->B ; A->D; A->C ; D->C ; A->C"
n=3, "A->C ; A->C ; A->B ; C->B ; A->C ; B->A ; B->C ; A->C"
n=3, "A->C ; A->B ; C->B ; A->B ; B->C ; B->A ; B->C ; A->C"
n=3, "A->C ; A->B ; C->B ; A->C ; B->A ; B->C ; C->B"
n=4, "A->B ; A->C ; B->C ; A->B ; C->A ; C->B ; A->B ; A->C ; B->C ; B->A ; C->A ; B->C ; A->B ; A->C"
n=4, "A->B ; A->B ; A->B ; A->C ; B->C ; B->C ; B->C"`)
<pre id=O></pre>

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.