하노이의 탑 솔버


10

하노이 탑이 무엇인지에 관해서는 구글이나 위키 백과 페이지를 참조하십시오.

코드는 두 가지 작업을 수행 할 수 있어야하며 다음과 같습니다.

  • 하노이 타워의 시작점에서 디스크 수를 지정하는 사용자 입력 허용
  • 타워 퍼즐에 솔루션을 보여주기 위해 원하는 방식으로 출력을 만듭니다 (어떻게 든 논리적 인 한).

논리 출력의 예는 다음과 같습니다 (4 디스크 시작 사용).

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

L왼쪽 페그, C중앙 페그 R, 오른쪽 페그를 나타내며 숫자는 해당 페그에서 디스크를 얼마나 멀리 이동하고 어떤 방향으로하는지 나타냅니다. 양수는 디스크가 가장 왼쪽 페그에서 시작하기 때문에 가장 오른쪽 페그로 이동하는 페그 수를 나타냅니다.

하노이의 탑 규칙은 간단합니다 :

  • 한 번에 하나의 디스크 만 이동할 수 있습니다.
  • 각 이동은 페그 중 하나에서 상단 디스크를 가져 와서 해당 페그에 이미 존재할 수있는 다른 디스크의 상단으로 다른 페그로 밀어 넣는 것으로 구성됩니다.
  • 작은 디스크 위에는 디스크를 놓을 수 없습니다.

디스크는 가장 왼쪽 페그에서 시작합니다. 맨 아래에서 가장 크고 맨 위에서 가장 작습니다.


우리는 임의로 큰 타워를 해결해야합니까, 아니면 10, 100, 1k, 1M 디스크와 같이 가정 할 수있는 한계가 있습니까?
사용자 알 수 없음

@userunknown 내가 너라면, 엄청나게 많은 수에 대해 너무 걱정하지 않을 것이지만, 프로그램이 처리 할 수있는 최대 디스크 수는 컴퓨터의 메모리 용량이나 호출 스택 제한 ( 기억은 꽤 일반적인 용어이므로) 코드를 제출할 때 임의로 높은 숫자가 당신을 놀라게하지 마십시오. 귀하의 솔루션이 독창적이지만 너무 많은 디스크 만 처리 할 수 ​​있다면, 나는 여전히 당신에게 신용을 줄 것입니다.
카터 파페 1

글쎄, 내 생각은 꽤 비효율적 인 해결 알고리즘이었고 한계가 있다면 프로그램이 처리 할 수 ​​있으면 좋을 것입니다. 그러나 지금까지 솔루션을 살펴본 결과 완전히 다른 리그에서 뛸 수 있다는 것을 깨달았습니다.
사용자가 알 수 없음

답변:


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

파이썬, 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 문자를 면도합니다 : tio.run/##K6gsycjPM/r/…
JayCe

6

펄-54 자

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

perl -M5.010stdin에서 실행하여 디스크 수를 입력하십시오.

출력 형식:

이동 당 한 줄, 첫 번째 숫자는 페그에서, 두 번째 숫자는 페그에서 (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<<<>
marinus

5

GolfScript ( 31 25 24 자)

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

Ilmari Karonen 덕분에 원래 tr의 s / permutations가 6 문자로 단축 될 수 있음 을 지적했습니다 . 그것들을 두 개의 순열의 곱으로 분해함으로써 하나 더 절약 할 수있었습니다.

3%증가 길이를 한 문자 씩 고려하면 다음과 같습니다.

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

어떤 사람들은 실제로 복잡한 출력 형식을 가지고 있습니다. 이렇게하면 페그가 (0, 1, 2 번)에서 이동하고 페그가로 이동합니다. 스펙은 이동할 페그를 말하지 않으므로 페그 1로 이동합니다.

예 :

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

의심의 여지없이 sed의 동일한 논리가 더 짧지 만 내 sed 능력은 그것에 달려 있지 않습니다.
피터 테일러

1
당신은 25 문자로 할 수 있습니다 :])~{.{3^3%}%2,@{2\-}%++}*
Ilmari Karonen

3

펄, 75 79

Keith Randall의 출력 형식을 완전히 훔칩니다.

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 기능을 활성화하는 비용을 포함시키지 않아도됩니까?
breadbox

1
당신은 — 하지만 무료입니다. Perl 호출에 유창하지 않은 사람들이 어쨌든 샷을 줄 수 있도록 프로그램을 호출하는 방법을 적어 두십시오.
JB

링크 주셔서 감사합니다; 나는 그런 종류의 것을 더 일찍 찾고 있었다.
breadbox

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 개의 디스크
  • 시작점
  • 목표 포인트

2

배쉬 (64 문자)

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

t역할을 다시 사용하는 것을 좋아하기 때문에 GolfScript 길이의 두 배 이상이되었지만 이것을 게시하는 것입니다 echo $s.


2

스칼라, 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)

출력 형식

디스크 수 = 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를 잘 사용합니다.
피터 테일러

2

C, 98 92 87 자

가장 사소한 알고리즘을 구현합니다.
출력은 ab ab ab각 쌍이 "톱 디스크를 페그 a에서 페그 b로 이동"을 의미 하는 형태 입니다.
편집 : 움직임은 이제 16 진수로 인코딩됩니다-0x12는 페그 1에서 페그 2 로의 이동을 의미합니다.
편집 : 매개 변수가 아닌 stdin에서 숫자를 읽습니다. 더 짧습니다.
예:
% 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 ()의 본문 구문을 설명 할 수 있습니까? 특히 재귀 호출 (d ^ d % 16 * 16 및 printf (...))에서 명백한 두 가지 인수와 마지막 작업이 분명히 끝났습니다. 내 지식에 따르면 그 함수에는 두 가지 구문 오류가 있지만 이미 stdio를 포함하여 빌드하고 올바르게 실행한다는 것을 알고 있습니다.
Griffin

1
함수가 원하는 것보다 많은 매개 변수를 전달할 수 있습니다. 그들의 가치는 어디에도 없습니다. h(x,printf(...))단순히 전화 printf하기 전에 호출하는 방법 h입니다. 마지막 n++은 내부 h반환 후 이루어집니다 . initial을 취소하는 데 사용됩니다 n--.
ugoren

고마워, 그것은 말이됩니다 (n ++의 목적은 분명했습니다). 쉼표 대신 n ++ 앞에 세미콜론이없는 이유는 무엇입니까? 아니면 차이가 있습니까?
Griffin

@Griffin, 사실 ;여기는 동일합니다. ,종종 유용 하지만 (예 : if(x)a,b;대체 if(x){a;b;}) 이점이 없습니다.
ugoren

2

젤리 , 5 바이트

2*Ṗọ2

온라인으로 사용해보십시오!

0가장 작은 디스크를 한
1칸 오른쪽으로 이동 ( 필요한 경우 시작으로 다시 감음) 두 번째로 작은 디스크를 다른 법적 열로
2이동 세 번째로 작은 디스크를 다른 법적 열로 이동
등에

연산

하노이 타워 문제의 해결책을 재귀 적으로 볼 수 있습니다. 크기의 스택을 이동 N을 으로부터 에 B하는 크기의 스택을 이동 N -1에서 에 C 다음 크기의 디스크 N 에서 에 B가 다음 크기의 스택을 이동 N -1에서 BC . 이것은 다음 형식의 패턴을 생성합니다 (이 프로그램에서 사용하는 출력 형식으로).

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

우리는이 순서가 OEIS에서 A007814 임을 알 수 있습니다 . 시퀀스의 또 다른 가능한 정의는 "시퀀스의 k 번째 (1 기반) 요소는 숫자 k 의 끝에서 0의 수입니다. 이진수로 기록 될 때 "이다. 그리고 그것이 여기 프로그램이 계산하는 것입니다.

설명

먼저, 솔루션의 이동 수, 2 n -1 을 계산합니다 . 실제로 한 번의 추가 이동을 계산하고 나중에 버리는 것이 가장 짧습니다.2* 2의 값입니다. (지금까지 우리가 취한 유일한 입력은 명령 행 인수이므로 기본적으로 사용됩니다.)

다음으로, 우리는 기본 b 의 숫자 끝에서 0의 수를 계산하기 위해 Jelly의 내장을 사용합니다 . 입니다 . 이진수로 계산할 때는 입니다. 이 내장을 1에서 2 n -1까지 의 숫자에 적용하기 만하면됩니다 .bọ2

Jelly에서 여러 숫자를 반복하는 간단한 두 가지 방법이 있으며 R,이 문제에 대한 나의 초기 시도는이 중 하나를 사용했습니다. 그러나이 경우 약간 더 짧을 수 있습니다 . 이것이 바로이 경우에 우리가 원하는 것입니다 (왜냐하면2* 너무 많은 elments를 생성) 그래서 링크를 사용하여, 2*그리고 ọ2으로는 2*Ṗọ2우리에게 문제에 대한 5 바이트 솔루션을 제공합니다.


1

어, 72 자

function t(n,a,b){if(n){t(n-1,a,a^b);print n,a,b;t(n-1,a^b,b)}}t($0,1,3)

출력 형식은 Keith Randall 과 동일 합니다.

awk -f tower.awk <<< "3"    
1 1 1
2 1 1
1 1 1
3 1 3
1 1 1
2 1 3
1 1 3

1

배쉬 스크립트, 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

편집 : 피터 의 의견에 의해 4 개의 문자를 저장했습니다 .


1
반향 $@
Peter Taylor

@ PeterTaylor : 좋은 지적입니다. 업데이트하겠습니다.
프린스 존 웨슬리

1

J, 23 바이트

이진수 솔루션

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

이 솔루션은 이 비디오에 설명 이진 계산 방법을 사용합니다 .

즉, 1최대 2 진수를 생성 2^n한 다음 길이 2의 접두사를 가져와 각 비트를 이전 숫자의 해당 비트와 비교하여 동일하지 않은지 확인합니다. 동일하지 않은 비트 수는 해당 이동에 대한 출력입니다.

가장 작은 디스크가 1로 레이블 된 3 개의 디스크에 대한 출력 (예 :

1 2 1 3 1 2 1

1 "가장 작은 디스크를 한 페그 오른쪽으로 옮기고 필요한 경우 첫 번째 페그로 반복합니다"

n다른 모든 경우 n" n합법적 인 말뚝 으로 디스크 이동"을 의미합니다 (항상 정확히 하나가 있어야 함).

온라인으로 사용해보십시오!

재귀 솔루션

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

위의 솔루션과 동일한 출력이지만 여기서 논리는 문제의 재귀 특성을 더 명확하게 만듭니다.

트리로 시각화하면이 점을 강조합니다.

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

온라인으로 사용해보십시오!


1
원래 질문이 5 년 전에 제출 된이 질문에 대한 답변을 검토하기 위해 돌아온 시간과 같은 시간 내에 원래 질문이 제출 된 후 5 년 동안 귀하의 답변을 제출하는 것은 우연의 일치입니다 ... 와우. +1
Carter Pape



0

R , 73 바이트

지도에 R을 넣습니다. [Keith Randall 's answer] [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

자바 스크립트 (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를 사용하여 페그 A에서 페그 C로 4 개의 디스크 이동)

리턴 'ABACBCABCACBABACBCBACABCABACBC'(페그 A에서 페그 B로 디스크 이동, 페그 A에서 페그 C로 디스크 이동, 페그 B에서 페그 C로 디스크 이동 등)


1
좋은. f, a, t 매개 변수에 함수 정의에 기본값이 포함되어야하는지 궁금합니다. 그렇지 않으면 제출시 추가 인수에 임의의 데이터가 포함될 수 있습니다. 그래도 초보자이므로 더 경험이 많은 사람이 조언해야합니다.
John Rees
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.