Tháp giải quyết hà nội


10

Để tham khảo về tòa tháp của Hà Nội là gì, hãy Google hoặc tìm trên trang Wikipedia .

Mã của bạn sẽ có thể thực hiện 2 điều và chúng là như sau:

  • Chấp nhận đầu vào của người dùng chỉ định số lượng đĩa tại điểm bắt đầu của tháp Hà Nội
  • Tạo đầu ra theo cách bạn chọn (miễn là bằng cách nào đó hợp lý) để hiển thị giải pháp cho câu đố tháp.

Một ví dụ về đầu ra logic sẽ là như sau (sử dụng khởi động 4 đĩa):

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

Lđại diện cho chốt bên trái, Cđại diện cho chốt trung tâm và Rđại diện cho chốt bên phải và các con số là khoảng cách để di chuyển đĩa trên chốt đó và theo hướng nào. Số dương biểu thị số lượng chốt di chuyển về phía chốt ngoài cùng bên phải (vì các đĩa bắt đầu ở chốt ngoài cùng bên trái).

Các quy tắc để tháp của Hà Nội rất đơn giản:

  • Mỗi lần chỉ có thể di chuyển một đĩa.
  • Mỗi lần di chuyển bao gồm lấy đĩa trên từ một trong các chốt và trượt nó vào một chốt khác, bên trên các đĩa khác có thể đã có trên chốt đó.
  • Không có đĩa có thể được đặt trên đầu của một đĩa nhỏ hơn.

Các đĩa bắt đầu ở chốt ngoài cùng bên trái, lớn nhất ở phía dưới, nhỏ nhất ở trên cùng, một cách tự nhiên.


Chúng ta có cần phải giải quyết các tòa tháp lớn tùy ý, hoặc có một số giới hạn mà chúng ta có thể giả sử, như các đĩa 10, 100, 1k, 1M không?
người dùng không biết

@userunknown nếu tôi là bạn, tôi sẽ không lo lắng quá nhiều về số lượng cực lớn, nhưng tôi sẽ nói rằng số lượng đĩa cao nhất mà chương trình của bạn có thể xử lý chỉ nên bị giới hạn bởi dung lượng bộ nhớ của máy tính hoặc giới hạn ngăn xếp cuộc gọi của nó ( Tôi đoán giống như vậy, vì bộ nhớ là một thuật ngữ khá chung chung). Mặc dù vậy, đừng để những con số cao tùy tiện làm bạn sợ khi gửi mã của bạn; nếu giải pháp của bạn là sáng tạo nhưng chỉ có thể xử lý rất nhiều đĩa, tôi sẽ vẫn cung cấp cho bạn tín dụng.
Carter Pape

Vâng, ý tưởng của tôi là một thuật toán giải quyết khá kém hiệu quả, và nếu giới hạn là, chương trình có thể xử lý, nó sẽ ổn. Nhưng tôi đã xem xét các giải pháp cho đến nay và nhận ra rằng tôi sẽ chơi ở một giải đấu hoàn toàn khác.
người dùng không xác định

Câu trả lời:


2

Husk , 5 byte

↑≠⁰İr

Hãy thử trực tuyến!
Mỗi ntrong đầu ra đại diện cho đĩa di chuyển nđến chốt có sẵn tiếp theo (gói theo chu kỳ).

Giải trình

   İ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 ký tự

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)

Chẳng hạn, với N = 3, nó trả về:

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

Tôi biết tôi đến trò chơi hơi muộn nhưng trò chơi này sẽ thay đổi 13 ký tự: tio.run/##K6gsycjPM/r/ Kẻ
JayCe

6

Perl - 54 ký tự

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

Chạy với perl -M5.010và nhập số lượng đĩa trên stdin.

Định dạng đầu ra:

Một dòng trên mỗi lần di chuyển, chữ số đầu tiên là từ chốt, chữ số thứ hai là chốt (bắt đầu từ 0)

Thí dụ:

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

Lưu 5 ký tự bằng cách tháo niềng răng. $x=--$_&-$_,say(($_-$x)%3,($_+$x)%3)for 2..1<<<>
bến tàu

5

GolfScript ( 31 25 24 ký tự)

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

Cảm ơn Ilmari Karonen đã chỉ ra rằng trs / hoán vị ban đầu của tôi có thể được rút ngắn bằng 6 ký tự. Bằng cách phân tách chúng như một sản phẩm của hai hoán vị, tôi đã cố gắng tiết kiệm thêm một lần nữa.

Lưu ý rằng bao thanh toán 3%tăng chiều dài của một ký tự:

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

Một số người có định dạng đầu ra thực sự phức tạp. Điều này xuất ra chốt được di chuyển từ (được đánh số 0, 1, 2) và chốt được di chuyển đến. Thông số kỹ thuật không cho biết chốt nào sẽ di chuyển, vì vậy nó di chuyển đến chốt 1.

Ví dụ

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

Không còn nghi ngờ gì nữa, logic tương tự trong sed thậm chí còn ngắn hơn, nhưng khả năng sed của tôi không phụ thuộc vào nó.
Peter Taylor

1
Bạn có thể làm điều đó trong 25 ký tự:])~{.{3^3%}%2,@{2\-}%++}*
Ilmari Karonen

3

Perl, 75 79 ký tự

Hoàn toàn ăn cắp định dạng đầu ra của 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

Gọi với -M5.010cho say.

(Tôi nghĩ rằng điều này có thể được cải thiện nếu bạn có thể tìm cách sử dụng giá trị trả về của hàm thay vì chỉ triệt tiêu nó.)


[ saykhuyến nghị "chỉ sử dụng " cổ phiếu ]
JB

Được rồi - nhưng tôi sẽ không bao gồm chi phí kích hoạt các tính năng 5.10 so với số char của tôi chứ?
hộp bánh mì

1
Bạn sẽ có thể nhưng nó miễn phí. Chỉ cần ghi chú về cách gọi chương trình của bạn để mọi người không thông thạo các chi tiết về yêu cầu perl có thể cung cấp cho nó một shot nào.
JB

Cảm ơn các liên kết; Tôi đã tìm kiếm loại điều đó sớm hơn.
hộp bánh mì

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);

chức năng gọi f(n,s,t)với:

  • n số đĩa
  • điểm bắt đầu
  • điểm mục tiêu

2

Bash (64 ký tự)

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

Đăng bài này mặc dù dài hơn hai lần so với GolfScript vì tôi thích việc tái sử dụng tđể phục vụ như echo $s.


2

Scala, 92 88 87 ký tự

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)

Định dạng đầu ra

Nói số đĩa = 3 rồi,

(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

Sử dụng tốt của xor.
Peter Taylor

2

C, 98 92 87 ký tự

Thực hiện các thuật toán tầm thường nhất.
Đầu ra ở dạng ab ab abmỗi cặp có nghĩa là "di chuyển đĩa trên cùng từ chốt a sang chốt b".
EDIT : Di chuyển hiện được mã hóa thành hex - 0x12 có nghĩa là di chuyển từ chốt 1 sang chốt 2. Đã lưu một số ký tự.
EDIT : Đọc số từ stdin, thay vì tham số. Ngắn hơn.
Thí dụ:
% 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);}

Ai đó có thể giải thích cú pháp của thân hàm h () - đặc biệt là hai đối số rõ ràng trong lệnh gọi đệ quy của nó (d ^ d% 16 * 16 và printf (...)), và thao tác cuối cùng dường như bị treo ở cuối. Dựa trên kiến ​​thức của tôi, hàm đó có hai lỗi cú pháp, nhưng tôi đã biết nó xây dựng (sau khi bao gồm stdio) và thực thi chính xác.
Griffin

1
Có thể truyền nhiều tham số hơn hàm muốn. Giá trị của họ không đi đến đâu. h(x,printf(...))chỉ đơn giản là một cách để gọi printftrước khi hđược gọi. Việc cuối cùng n++được thực hiện sau khi htrở về bên trong . Nó được sử dụng để hoàn tác ban đầu n--.
ugoren

Cảm ơn, điều đó có ý nghĩa (mục đích của n ++ là hiển nhiên). Tại sao không có dấu chấm phẩy đi trước n ++ thay vì dấu phẩy hoặc nó tạo ra sự khác biệt?
Griffin

@Griffin, Thật ra ;sẽ giống nhau ở đây. ,thường hữu ích (ví dụ if(x)a,b;thay thế if(x){a;b;}), nhưng không có lợi thế ở đây.
ugoren

2

Thạch , 5 byte

2*Ṗọ2

Hãy thử trực tuyến!

0di chuyển đĩa nhỏ nhất một khoảng trống sang phải (gói trở lại bắt đầu nếu cần)
1di chuyển đĩa nhỏ thứ hai sang cột hợp pháp khác
2di chuyển đĩa nhỏ thứ ba sang cột hợp pháp khác,
v.v.

Thuật toán

Chúng ta có thể thấy giải pháp của vấn đề Tháp Hà Nội một cách đệ quy; để di chuyển một chồng kích thước n từ A đến B , di chuyển một chồng kích thước n -1 từ A đến C , sau đó các đĩa kích thước n từ A đến B , sau đó di chuyển một chồng kích thước n -1 từ B đến C . Điều này tạo ra một mẫu có dạng sau (ở định dạng đầu ra được sử dụng bởi chương trình này):

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

Chúng ta có thể quan sát rằng chuỗi này là A007814 trên OEIS. Một định nghĩa có thể khác của chuỗi là "phần tử thứ k (dựa trên 1) của chuỗi là số lượng số 0 ở cuối số k khi nó được viết dưới dạng nhị phân". Và đó là những gì chương trình ở đây đang tính toán.

Giải trình

Đầu tiên, chúng tôi tính số lần di chuyển trong giải pháp, 2 n -1. Hóa ra là ngắn nhất để thực sự tính toán thêm một bước di chuyển và loại bỏ nó sau đó, vì vậy đây là 2*, tức là 2 với sức mạnh của một cái gì đó. (Đầu vào duy nhất chúng tôi đã thực hiện cho đến nay là đối số dòng lệnh, do đó, được sử dụng theo mặc định.)

Tiếp theo, chúng tôi sử dụng nội dung của Jelly để tính số lượng số 0 ở cuối một số trong cơ sở b ; rằng nhân . Như chúng ta đang tính toán trong hệ nhị phân, nó . Tất cả những gì chúng ta cần làm là áp dụng nội dung này cho các số từ 1 đến 2 n -1.bọ2

Có hai cách đơn giản để lặp lại một loạt các số trong Jelly, R, những nỗ lực trước đây của tôi về vấn đề này đã sử dụng một trong những cách này. Tuy nhiên, trong trường hợp này, có thể rút ngắn hơn một chút: khi được đưa ra một số làm đầu vào, sẽ cho phép bạn thực hiện một phép lặp dừng một phần tử ngắn (nói chung, là một phần tử được sử dụng để xử lý tất cả trừ một phần tử của một thứ gì đó). Đó chính xác là những gì chúng ta muốn trong trường hợp này (vì 2*tạo ra một quá nhiều elments), do đó sử dụng nó để liên kết 2*ọ2thành 2*Ṗọ2cho chúng ta một giải pháp 5 byte cho vấn đề.


1

Awk, 72 ký tự

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)

Định dạng đầu ra giống như định dạng của 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

Kịch bản Bash, 100 96 ký tự

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

Định dạng đầu ra giống như định dạng của Keith Randall .

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

Chỉnh sửa : Đã lưu 4 ký tự bằng nhận xét của peter .


1
Bạn có thể thêm khoảng trắng và lưu một vài ký tự bằng cách lặp lại$@
Peter Taylor

@PeterTaylor: Điểm tốt. Hãy để tôi cập nhật nó.
Hoàng tử John Wesley

1

J, 23 byte

giải pháp số nhị phân

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

Giải pháp này sử dụng phương pháp đếm nhị phân được mô tả trong video này .

Có nghĩa là, tôi tạo các chữ số nhị phân từ 1tối đa 2^n, sau đó lấy các phần tử có độ dài 2 và so sánh từng bit với bit tương ứng của số trước đó và kiểm tra xem chúng có bằng nhau không. Số lượng bit không bằng nhau là đầu ra cho di chuyển đó.

Đầu ra, ví dụ, cho 3 đĩa, trong đó đĩa nhỏ nhất được dán nhãn 1:

1 2 1 3 1 2 1

1 có nghĩa là "di chuyển đĩa nhỏ nhất một chốt bên phải, lặp lại chốt đầu tiên nếu cần"

n, đối với tất cả n, có nghĩa là "di chuyển đĩa nđến một chốt hợp pháp" (sẽ luôn có chính xác một)

Hãy thử trực tuyến!

giải pháp đệ quy

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

Cùng một đầu ra như giải pháp trên, nhưng logic ở đây làm cho tính chất đệ quy của vấn đề rõ ràng hơn.

Hình dung nó như một cái cây cũng nhấn mạnh điểm này:

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

Hãy thử trực tuyến!


1
bản chất ngẫu nhiên của việc gửi câu trả lời của bạn trong hơn 5 năm sau khi câu hỏi ban đầu được đặt ra trong cùng một giờ mà tôi quay lại để xem lại câu trả lời cho câu hỏi này tôi đã gửi hơn 5 năm trước khi wow wow. +1
Carter Pape



0

R , 73 byte

Đưa R lên bản đồ. Lấy cảm hứng từ [câu trả lời của Keith Randall] [1] với đầu vào được đơn giản hóa, chỉ in kết thúc và bắt đầu chốt để lưu 2 byte. Cũng chốt 0 chỉ mục.

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)}}

Hãy thử trực tuyến!


0

JavaScript (ES6), 45b

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

ví dụ: gọi h(4, 'A', 'B', 'C')(di chuyển 4 đĩa từ chốt A sang chốt C bằng cách sử dụng chốt phụ B)

trả về 'ABACBCABCACBABACBCBACABCABACBC'(di chuyển đĩa từ chốt A sang chốt B, di chuyển đĩa từ chốt A sang chốt C, di chuyển đĩa từ chốt B sang chốt C, v.v.)


1
Đẹp. Tôi tự hỏi liệu các tham số f, a, t có nên có mặc định trong định nghĩa hàm không? Nếu không, đệ trình có thể bao gồm dữ liệu tùy ý trong các đối số bổ sung. Tôi là một người mới, vì vậy một người có kinh nghiệm hơn nên khuyên.
John Rees
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.