Hanoi çözücü Kulesi


10

Hanoi kulesinin ne olduğu ile ilgili olarak, Google'da ya da Wikipedia sayfasına bakın.

Kodunuz 2 şey yapabilmelidir ve bunlar şunlardır:

  • Hanoi kulesinin başlangıç ​​noktasındaki disk sayısını belirten kullanıcı girişini kabul edin
  • Kule bulmacasına çözümü göstermek için seçtiğiniz bir şekilde çıktı oluşturun (bir şekilde mantıklı olduğu sürece).

Mantıksal çıktıya örnek olarak şunlar verilebilir (4 diskli bir başlatma kullanarak):

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

Lsol kancayı Ctemsil eder, merkez kancayı Rtemsil eder ve sağ kancayı temsil eder ve sayılar diski o sapta ne kadar ve ne yönde hareket ettireceğidir. Pozitif sayılar, en sağdaki sabitleyiciye doğru hareket eden mandal sayısını temsil eder (çünkü diskler en soldaki sabitleyicide başlar).

Hanoi kuleye kuralları basittir:

  • Bir seferde yalnızca bir disk taşınabilir.
  • Her hareket, üst diski mandallardan birinden alıp, bu mandal üzerinde mevcut olabilecek diğer disklerin üzerine başka bir manivelaya kaydırarak oluşur.
  • Daha küçük bir diskin üzerine disk yerleştirilemez.

Diskler en soldaki sapta başlar, en altta en büyük, en küçükte en küçük, doğal olarak.


Keyifli büyük kuleleri çözmemiz gerekiyor mu yoksa 10, 100, 1k, 1M diskler gibi üstlenebileceğimiz bir sınır var mı?
kullanıcı bilinmiyor

@ userunknown, ben olsaydım, çok büyük sayılar için çok fazla endişelenmezdim, ancak programınızın işleyebileceği en yüksek disk sayısının yalnızca bilgisayarın bellek kapasitesi veya çağrı yığını sınırı ( sanırım aynı şey, çünkü bellek oldukça genel bir terimdir). Bununla birlikte, kodunuzu gönderirken keyfi olarak yüksek sayıların sizi korkutmasına izin vermeyin; çözümünüz yaratıcıysa ancak yalnızca bu kadar çok diski işleyebiliyorsa, biri için hala size kredi veririm.
Carter Pape

Benim fikrim oldukça verimsiz bir çözme algoritmasıydı ve eğer sınır ise, programın üstesinden gelebiliyorsa, iyi olurdu. Ama şu ana kadar çözümlere baktım ve tamamen farklı bir ligde oynayacağımı fark ettim.
kullanıcı bilinmiyor

Yanıtlar:


2

Kabuk , 5 bayt

↑≠⁰İr

Çevrimiçi deneyin! Çıktıdaki
her bir ndisk n, bir sonraki kullanılabilir sabitleyiciye (döngüsel olarak sarma) hareket eden diski temsil eder .

açıklama

   İ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 karakter

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)

Örneğin, N = 3 için şunu döndürür:

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

Ben oyuna biraz geç kaldığımı biliyorum ama bu 13 karakter tıraş: tio.run/##K6gsycjPM/r/…
JayCe

6

Perl - 54 karakter

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

İle çalıştırın perl -M5.010ve stdin disklerin numarasını girin.

Çıkış biçimi:

Her hareket için bir satır, ilk basamak peg'den, ikinci basamak peg'den (0'dan başlayarak)

Misal:

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

Diş tellerini çıkararak 5 karakter kaydedin. $x=--$_&-$_,say(($_-$x)%3,($_+$x)%3)for 2..1<<<>
marinus

5

GolfScript ( 31 25 24 karakter)

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

İlmari Karonen'e, orijinal trs / permütasyonlarımın 6 karakterle kısaltılabileceğine işaret ettiği için teşekkürler . Onları iki permütasyonun bir ürünü olarak çürüterek bir tane daha kurtarmayı başardım.

Dikkate almanın 3%uzunluğu bir karakter arttırdığını unutmayın :

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

Bazı insanlar gerçekten karmaşık çıktı biçimlerine sahiptir. Bu, sabitleyicinin (0, 1, 2 numaralı) taşındığı ve sabitleyicinin konumuna taşındığını verir. Spesifikasyon hangi pimin hareket edeceğini söylemez, bu nedenle peg 1'e gider.

Örneğin

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

Şüphesiz sed'deki aynı mantık daha da kısadır, ancak sed yeteneklerim buna bağlı değildir.
Peter Taylor

1
25 karakterle yapabilirsiniz:])~{.{3^3%}%2,@{2\-}%++}*
Ilmari Karonen

3

Perl, 75 79 karakter

Keith Randall'ın çıktı formatını tamamen çalmak:

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

İle çağırır -M5.010için say.

(Yalnızca bastırmak yerine işlevin dönüş değerini kullanmanın bir yolunu bulabilirseniz, bunun geliştirilebileceğini düşünüyorum.)


[stok "sadece kullan say" önerisi]
JB

Tamam - ancak karakter sayımla 5.10 özelliği etkinleştirme maliyetini dahil etmem gerekmez mi?
ekmek kutusu

1
Yapacaksın - ama ücretsiz. Sadece programınızı nasıl çağıracağınızı not edin, böylece perl invocation spesifikasyonlarında akıcı olmayan insanlar yine de bir şans verebilir.
JB

Bağlantı için teşekkürler; Daha önce böyle bir şey arıyordum.
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);

çağrı fonksiyonu f(n,s,t):

  • n disk sayısı
  • s başlangıç ​​noktası
  • t hedef noktası

2

Bash (64 karakter)

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

GolfScript uzunluğunun iki katından fazla olmasına rağmen bunu yayınlamak çünkü thizmet etmek için yeniden kullanmayı seviyorum echo $s.


2

Scala, 92 88 87 karakter

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)

Çıkış biçimi

Disk sayısını söyle = 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 güzel kullanımı.
Peter Taylor

2

C, 98 92 87 karakter

En önemsiz algoritmayı uygular.
Çıktı, ab ab abher çiftin "üst diski peg a'dan peg b'ye hareket ettirme" anlamına geldiği biçimdedir .
EDIT : hamle şimdi hex kodlanmıştır - 0x12 peg 1 peg 2 taşımak anlamına gelir. Bazı characeters kaydedildi.
EDIT : Numarayı parametre yerine stdin'den okur. Daha kısa.
Misal:
% 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);}

Birisi h () işlevinin sözdizimini - özellikle özyinelemeli çağrısında görünen iki argümanı (d ^ d% 16 * 16 ve printf (...)) ve son işlemin görünüşte sona erdiğini açıklayabilir. Bildiğim kadarıyla, bu işlevin iki sözdizimi hatası vardır, ancak zaten (stdio dahil olduktan sonra) kurulduğunu ve doğru çalıştığını biliyorum.
Griffin

1
Fonksiyonun istediğinden daha fazla parametre iletmek mümkündür. Değerleri hiçbir yere gitmiyor. h(x,printf(...))daha printfönce çağırmanın bir yoludur h. Sonuncusu n++hdönüşlerden sonra yapılır . İlkini geri almak için kullanılır n--.
ugoren

Teşekkürler, bu mantıklı (n ++ 'ın amacı belliydi). Neden virgül yerine n ++ 'dan önce gelen noktalı virgül yok ya da bir fark yaratıyor mu?
Griffin

@Griffin, Aslında ;burada da aynı olurdu. ,genellikle yararlıdır (örneğin if(x)a,b;yerine geçer if(x){a;b;}), ancak burada bir avantajı yoktur.
ugoren

2

Jöle , 5 bayt

2*Ṗọ2

Çevrimiçi deneyin!

0en küçük diski bir boşluk sağa taşı (gerekirse başlangıca geri sarma)
1en kük diski diğer yasal sütuna
2taşıyın, en küçük diski diğer yasal sütuna taşıyın
.

Algoritma

Hanoi Kuleleri sorununun çözümünü özyineli olarak görebiliriz; boyutuna sahip bir yığını taşımak için n den A için B , boyutta bir yığını taşımak N -1 ila A için C , o zaman boyutu disk n den A için B , daha sonra boyutta bir yığını taşımak N -1 ila B için C . Bu, aşağıdaki formda bir model oluşturur (bu program tarafından kullanılan çıktı biçiminde):

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

Bu dizinin OEIS'de A007814 olduğunu gözlemleyebiliriz . Sekansın başka bir olası tanımı "olan K th (1 bazlı) dizisinin elemanı sayısı sonunda sıfır sayısıdır k ikili olarak yazılır." Ve buradaki program bunu hesaplıyor.

açıklama

İlk olarak, çözelti içindeki hareket sayısını hesaplıyoruz, 2 n -1. Bir ekstra hareketi gerçekten hesaplamak ve daha sonra atmak için en kısa olduğu ortaya çıkıyor, bu yüzden bu, bir 2*şeyin gücüne 2. (Şimdiye kadar aldığımız tek girdi komut satırı bağımsız değişkenidir, bu yüzden varsayılan olarak kullanılır.)

Daha sonra, b tabanındaki bir sayının sonundaki sıfır sayısını hesaplamak için Jelly yerleşimini kullanırız ; bu kadar . İkili olarak hesapladığımız için . Tek yapmamız gereken bu yerleşkeyi 1 ila 2 n -1 dahil sayılara uygulamaktır .bọ2

Orada yineleme iki kolay yolu Jelly içinde sayı aralığıyla bitti, ve R, ve bu sorunun benim daha önceki girişimleri bunlardan birini kullandı. Bununla birlikte, bu durumda, biraz daha kısa gitmek mümkündür: giriş olarak bir sayı verildiğinde, bir öğeyi kısa durduran bir yineleme yapmanıza izin verir (genel olarak, bir şeyin dışındaki bir öğeyi işlemek için kullanılan bir yerleşiktir). Bu durumda tam olarak istediğimiz şey budur (çünkü 2*çok fazla seçim üretir), bu yüzden onu bağlamak 2*ve ọ2içine kullanmak 2*Ṗọ2bize soruna 5 baytlık bir çözüm verir.


1

Awk, 72 karakter

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)

Çıktı formatı Keith Randall'ınki ile aynıdır .

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

Bash senaryosu, 100 96 karakter

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

Çıktı formatı Keith Randall'ınki ile aynıdır .

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

Düzenleme : by Kaydedilen 4 karakter Peter un yorumunu.


1
Boşlukları ekleyebilir ve yankı yaparak birkaç karakter kaydedebilirsiniz$@
Peter Taylor

@PeterTaylor: İyi bir nokta. güncellememe izin ver.
Prens John Wesley

1

J, 23 bayt

ikili sayılar çözümü

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

Bu çözüm, bu videoda açıklanan ikili sayma yöntemini kullanır .

Demek ki, ikilik basamakları 1en fazla 2^n2 kadar üretiyorum, sonra 2 uzunluğundaki ekleri alıyorum ve her biti bir önceki sayının karşılık gelen bitiyle karşılaştırıyorum ve eşitsiz olup olmadıklarını kontrol ediyorum. Eşit olmayan bitlerin sayısı o hareketin çıktısıdır.

En küçük diskin 1 olarak etiketlendiği 3 disk için çıkış:

1 2 1 3 1 2 1

1 "gerekirse en küçük diski bir tane sağa hareket ettir, ilk deliğe geri döndür"

n, diğer herkes için n, "diski nyasal bir sabitleyiciye taşı" anlamına gelir (her zaman tam olarak bir tane olacaktır)

Çevrimiçi deneyin!

özyinelemeli çözüm

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

Yukarıdaki çözümle aynı çıktı, ancak buradaki mantık sorunun özyinelemeli doğasını daha net hale getirir.

Bir ağaç olarak görselleştirmek de bu noktayı vurgular:

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

Çevrimiçi deneyin!


1
orijinal soru sorulduktan sonra 5 yıl içinde yanıtınızı göndermenin tesadüf niteliği, 5 yıl önce sunduğum bu sorunun yanıtlarını gözden geçirmek için geri döndüm… vay. +1
Carter Pape



0

R, , 73 bayt

Haritaya R koyma. Basitleştirilmiş girişi ile [Keith Randall'ın cevabı] [1] 'den esinlenerek, sadece sonu yazdırın ve 2 bayt tasarruf etmek için peg'i başlatın. Ayrıca 0 indeksli mandal.

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

Çevrimiçi deneyin!


0

JavaScript (ES6), 45b

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

örneğin çağırma h(4, 'A', 'B', 'C')(yardımcı disk B kullanarak 4 diski A peg'ten C pegine taşıyın)

döner 'ABACBCABCACBABACBCBACABCABACBC'(bir diski A peg'ten B pegine taşıyın, bir diski A peg'ten C peg'e taşıyın, bir diski B peginden peg C'ye taşıyın, vb.)


1
Güzel. F, a, t parametrelerinin işlev tanımında varsayılanları içermesi gerekip gerekmediğini merak ediyorum. Aksi takdirde, gönderiler ek argümanlarda rastgele veriler içerebilir. Ben bir acemi değilim, bu yüzden daha deneyimli biri tavsiye etmelidir.
John Rees
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.