Башта вирішувача Ханой


10

Для ознайомлення з тим, що таке башта Ханої, або Google це, або дивіться на сторінці Вікіпедії .

Ваш код повинен вміти робити дві речі, а вони такі:

  • Прийміть введення користувача, яке визначає кількість дисків у початковій точці вежі Ханой
  • Створюйте вихід на свій вибір (доки це логічно), щоб показати рішення головоломки вежі.

Прикладом логічного виводу може бути наступний (із застосуванням запуску 4 диска):

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

Lпредставляє лівий кілочок, Cявляє собою центральний кілочок і Rпредставляє правий кілочок, а цифри - як далеко переміщати диск на цьому кілочку і в якому напрямку. Позитивні числа представляють кількість кілочків, що рухаються до крайнього правого кілка (тому що диски починаються на крайньому лівому кілочку).

У правилах до башти Ханоя прості:

  • Одночасно можна переміщувати лише один диск.
  • Кожен хід полягає в тому, щоб взяти верхній диск з одного з кілочків і перенести його на інший кілочок, поверх інших дисків, які вже можуть бути присутніми на цьому кілочку.
  • Жоден диск не може бути розміщений поверх меншого диска.

Диски починаються з крайнього лівого кілочка, найбільшого внизу, найменшого вгорі, природно.


Чи потрібно нам вирішувати довільно великі вежі, чи є якась межа, яку ми можемо припустити, як диски 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

Пітон, 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 годин
JayCe

6

Perl - 54 символи

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

Запустіть perl -M5.010і введіть кількість дисків на stdin.

Формат виводу:

Один рядок за ходом, перша цифра - від прив’язки, друга цифра - до кілки (починаючи з 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перестановки можуть бути скорочені на 6 символів. Розклавши їх як продукт двох перестановок, мені вдалося зберегти ще одну.

Зауважте, що розбиваючи на 3%довжину, збільшуйте довжину на один символ:

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

Деякі люди мають дуже складні вихідні формати. Це видає кілочок, переміщений з (пронумерований 0, 1, 2), і кілочок перемістився на Специфікація не говорить, до якого кілочка рухатись, тому він переходить до прив’язки 1.

Напр

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

Без сумніву, та ж логіка в sed ще коротша, але мої здібності sed не до цього.
Пітер Тейлор

1
Зробити це можна за 25 ])~{.{3^3%}%2,@{2\-}%++}*
годин

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

Дякуємо за посилання; Я шукав таку річ раніше.
хлібниця

3

СМЛ, 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, тому що мені подобається повторне використання tфункції 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".
EDIT : Рухи тепер закодовані в шістнадцять - 0x12 означає перехід від прив’язки 1 до кілка 2. Збережено деякі властивості.
EDIT : Читає число з 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) і виконує правильно.
Гріффін

1
Можна передавати більше параметрів, ніж хоче функція. Їх значення нікуди не діваються. h(x,printf(...))це просто спосіб зателефонувати printfдо того, hяк викликається. Останнє n++робиться після внутрішнього hповернення. Він використовується для відміни початкового n--.
угорен

Дякую, це має сенс (мета n ++ була очевидною). Чому немає крапки з комою, яка передує n ++ замість коми, або це має значення?
Гріффін

@Griffin, насправді тут ;було б те саме. ,часто корисний (наприклад, if(x)a,b;замінює if(x){a;b;}), але не має тут переваги.
ugoren

2

Желе , 5 байт

2*Ṗọ2

Спробуйте в Інтернеті!

0перемістіть найменший диск на один пробіл праворуч (при необхідності поверніть назад до початку),
1перемістіть другий найменший диск на єдиний інший законний стовпець,
2перемістіть третій-найменший диск на єдиний інший законний стовпець
тощо.

Алгоритм

Ми можемо бачити рішення проблеми Ханойської вежі рекурсивно; щоб перемістити стек розміром n від A до B , перемістіть стек розміром n -1 від A до C , потім диск розміром n від A до B , потім перемістіть стек розміром n -1 від B до C . Це створює шаблон наступної форми (у вихідному форматі, який використовується цією програмою):

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

Ми можемо спостерігати, що ця послідовність є A007814 на OEIS. Іншим можливим визначенням послідовності є " k й (1-заснований) елемент послідовності - це число нулів на кінці числа k коли воно записується двійковим". І ось що тут розраховує програма.

Пояснення

Спочатку обчислюємо кількість рухів у розчині, 2 n -1. Це виявляється найкоротшим, щоб насправді обчислити один додатковий хід і відкинути його пізніше, тому це так2* , тобто, 2 на щось. (Єдиний вхід, який ми взяли до цього часу, - це аргумент командного рядка, який використовується за замовчуванням.)

Далі ми використовуємо вбудований Jelly для обчислення кількості нулів в кінці числа в базі b ; ось це . Як ми обчислюємо у двійковій формі, це так . Все, що нам потрібно зробити, це застосувати цю вбудовану до чисел від 1 до 2 n -1 включно.bọ2

Існує два простих способи перерахування діапазону чисел в Jelly, і R, і мої попередні спроби цієї проблеми використовували один із них. Однак у цьому випадку можна піти трохи коротше: якщо вам введено число як вхід, ви дозволите зробити ітерацію, яка зупиняє один елемент короткою (загалом, це вбудований елемент, який використовується для обробки всіх, крім одного елемента чогось). Це саме те , що ми хочемо , щоб в цьому випадку (оскільки 2*генерують занадто багато елементів , які входять), тому використовувати його для зв'язку 2*і ọ2в 2*Ṗọ2дає нам 5-байтовое рішення проблеми.



1

Баш сценарій, 100 96 символів

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

Формат виходу такий же, як у формату Кіта Рендалла .

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

Edit : Збережено 4 символів на пітер коментар «s.


1
Ви можете додати пробіли та зберегти кілька символів, відлунюючи$@
Пітер Тейлор

@PeterTaylor: Добре. дозвольте мені оновити його.
Принц Джон Веслі

1

J, 23 байти

рішення двійкових чисел

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

У цьому рішенні використовується метод двійкового підрахунку, описаний у цьому відео .

Що означає, я генерую двійкові цифри від 1до2^n , потім беру інфікси довжиною 2 і порівнюю кожен біт із відповідним бітом попереднього числа та перевіряю, чи вони нерівні. Кількість нерівних бітів - вихід для цього ходу.

Вихід, наприклад, для 3-х дисків, де найменший диск позначений 1:

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 на карту. Натхненний [відповіддю Кіта Рандалла] [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')(перемістіть 4 диски з кілочка A на кілочок C за допомогою допоміжного кілочка B)

повертає 'ABACBCABCACBABACBCBACABCABACBC'(переміщення диска з кілочка А на кілочок В, переміщення диска з кілочка А на кілочок С, переміщення диска з кілочка В на кілочок С тощо)


1
Приємно. Цікаво, чи повинні параметри f, a, t включати стандартні параметри у визначення функції? В іншому випадку подання може включати довільні дані в додаткові аргументи. Я хоч і новачок, тому хтось досвідченіший повинен порадити.
Джон Різ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.