Solveur de la tour de Hanoi


10

Pour savoir ce qu'est la tour de Hanoi, recherchez-la sur Google ou regardez sur la page Wikipedia .

Votre code devrait pouvoir faire 2 choses, et ce sont les suivantes:

  • Acceptez l'entrée utilisateur qui spécifie le nombre de disques au point de départ de la tour de Hanoi
  • Créez une sortie de la manière de votre choix (tant qu'elle est en quelque sorte logique) pour montrer la solution au puzzle de la tour.

Un exemple de sortie logique serait le suivant (en utilisant un démarrage à 4 disques):

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

Lreprésente la cheville gauche, Creprésente la cheville centrale et Rreprésente la cheville droite et les nombres indiquent jusqu'où déplacer le disque sur cette cheville et dans quelle direction. Les nombres positifs représentent le nombre de chevilles se déplaçant vers la cheville la plus à droite (car les disques commencent sur la cheville la plus à gauche).

Les règles de la tour de Hanoi sont simples:

  • Un seul disque peut être déplacé à la fois.
  • Chaque mouvement consiste à prendre le disque supérieur de l'un des piquets et à le faire glisser sur un autre piquet, au-dessus des autres disques qui peuvent déjà être présents sur ce piquet.
  • Aucun disque ne peut être placé au-dessus d'un disque plus petit.

Les disques commencent sur la cheville la plus à gauche, le plus grand en bas, le plus petit en haut, naturellement.


Avons-nous besoin de résoudre des tours arbitrairement grandes, ou y a-t-il une limite que nous pouvons supposer, comme les disques 10, 100, 1k, 1M?
utilisateur inconnu

@userunknown si j'étais vous, je ne m'inquiéterais pas trop des nombres extraordinairement grands, mais je dirai que le plus grand nombre de disques que votre programme peut gérer ne devrait être limité que par la capacité mémoire de l'ordinateur ou sa limite de pile d'appels ( sorte de la même chose, je suppose, car la mémoire est un terme assez général). Cependant, ne laissez pas les nombres arbitrairement élevés vous effrayer lorsque vous soumettez votre code; si votre solution est créative mais ne peut gérer qu'un si grand nombre de disques, pour ma part, je vous donnerais tout de même du crédit.
Carter Pape

Eh bien, mon idée était un algorithme de résolution assez inefficace, et si la limite est, si le programme peut gérer, ce serait bien. Mais j'ai regardé les solutions jusqu'à présent et j'ai réalisé que je jouerais dans une ligue complètement différente.
utilisateur inconnu

Réponses:


2

Husk , 5 octets

↑≠⁰İr

Essayez-le en ligne!
Chacun ndans la sortie représente le déplacement du disque nvers la prochaine cheville disponible (encapsulation cyclique).

Explication

   İ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 caractères

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)

Par exemple, pour N = 3, il renvoie:

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

Je sais que je suis un peu en retard dans le jeu mais cela rase 13 caractères: tio.run/##K6gsycjPM/r/…
JayCe

6

Perl - 54 caractères

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

Exécutez avec perl -M5.010et entrez le nombre de disques sur stdin.

Format de sortie:

Une ligne par coup, le premier chiffre provient du piquet, le deuxième chiffre est du piquet (à partir de 0)

Exemple:

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

Enregistrez 5 caractères en supprimant les accolades. $x=--$_&-$_,say(($_-$x)%3,($_+$x)%3)for 2..1<<<>
marinus

5

GolfScript ( 31 25 24 caractères)

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

Merci à Ilmari Karonen d'avoir souligné que mes trpermutations originales pourraient être raccourcies de 6 caractères. En les décomposant comme un produit de deux permutations, j'ai réussi à en sauver une de plus.

Notez que la prise en compte de la 3%longueur augmente d'un caractère:

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

Certaines personnes ont des formats de sortie vraiment compliqués. Cela émet la cheville déplacée (numérotée 0, 1, 2) et la cheville déplacée vers. La spécification ne dit pas vers quelle cheville déplacer, elle se déplace donc vers la cheville 1.

Par exemple

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

Sans doute, la même logique dans sed est encore plus courte, mais mes capacités sed ne sont pas à la hauteur.
Peter Taylor

1
Vous pouvez le faire en 25 caractères:])~{.{3^3%}%2,@{2\-}%++}*
Ilmari Karonen

3

Perl, 75 79 caractères

Volant totalement le format de sortie de 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

Appelez avec -M5.010pour le say.

(Je pense que cela peut être amélioré si vous pouvez trouver un moyen d'utiliser la valeur de retour de la fonction au lieu de simplement la supprimer.)


[ sayrecommandation "il suffit d'utiliser "]
JB

D'accord, mais ne devrais-je pas inclure le coût d'activation des fonctionnalités 5.10 par rapport à mon nombre de caractères?
boîte à pain

1
Vous le feriez - mais c'est gratuit. Notez simplement comment invoquer votre programme afin que les personnes qui ne maîtrisent pas les détails de l'invocation de perl puissent de toute façon essayer.
JB

Merci pour le lien; Je cherchais ce genre de chose plus tôt.
boîte à pain

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

fonction d'appel f(n,s,t)avec:

  • n nombre de disques
  • s point de départ
  • point de but

2

Bash (64 caractères)

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

Publier celui-ci en dépit d'être plus de deux fois plus long que celui de GolfScript parce que j'aime la réutilisation de tpour servir de echo $s.


2

Scala, 92 88 87 caractères

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)

Format de sortie

Dites alors nombre de disque = 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

Belle utilisation de xor.
Peter Taylor

2

C, 98 92 87 caractères

Implémente l'algorithme le plus trivial.
La sortie est sous la forme ab ab aboù chaque paire signifie "déplacer le disque supérieur de la cheville a vers la cheville b".
EDIT : les mouvements sont maintenant encodés en hexadécimal - 0x12 signifie passer de la cheville 1 à la cheville 2. Sauvegardé quelques caractères.
EDIT : lit le nombre depuis stdin, plutôt que le paramètre. Plus court.
Exemple:
% 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);}

Quelqu'un peut-il expliquer la syntaxe du corps de la fonction h () - en particulier les deux arguments apparents dans son appel récursif (d ^ d% 16 * 16 et printf (...)), et la dernière opération semble suspendre la fin. D'après mes connaissances, cette fonction a deux erreurs de syntaxe, mais je sais déjà qu'elle se construit (après avoir inclus stdio) et s'exécute correctement.
Griffin

1
Il est possible de passer plus de paramètres que la fonction ne le souhaite. Leurs valeurs ne vont nulle part. h(x,printf(...))est simplement un moyen d'appeler printfavant d' happeler. Le dernier n++est fait après le hretour intérieur . Il sert à défaire l'initiale n--.
ugoren

Merci, cela a du sens (le but de n ++ était évident). Pourquoi n'y a-t-il pas un point-virgule précédant n ++ au lieu d'une virgule, ou cela fait-il une différence?
Griffin

@Griffin, En fait, ce ;serait la même chose ici. ,est souvent utile (par exemple if(x)a,b;remplace if(x){a;b;}), mais n'a aucun avantage ici.
ugoren

2

Gelée , 5 octets

2*Ṗọ2

Essayez-le en ligne!

0déplacer le plus petit disque d'un espace vers la droite (retour au début si nécessaire)
1déplacer le deuxième plus petit disque vers la seule autre colonne légale
2déplacer le troisième plus petit disque vers la seule autre colonne légale,
etc.

Algorithme

Nous pouvons voir la solution du problème des tours de Hanoi récursivement; pour déplacer une pile de taille n de A à B , déplacer une pile de taille n -1 à partir de A à C , le disque de taille n de A à B , puis déplacer une pile de taille n -1 à partir de B à C . Cela produit un modèle de la forme suivante (dans le format de sortie utilisé par ce programme):

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

Nous pouvons observer que cette séquence est A007814 sur l'OEIS. Une autre définition possible de la séquence est "l' élément k ème (basé sur 1) de la séquence est le nombre de zéros à la fin du nombre k lorsqu'il est écrit en binaire". Et c'est ce que le programme calcule ici.

Explication

Tout d'abord, nous calculons le nombre de mouvements dans la solution, 2 n -1. Il s'avère être le plus court pour réellement calculer un coup supplémentaire et le jeter plus tard, c'est-à- 2*dire 2 à la puissance de quelque chose. (La seule entrée que nous avons prise jusqu'à présent est l'argument de ligne de commande, de sorte qu'il est utilisé par défaut.)

Ensuite, nous utilisons la fonction intégrée de Jelly pour calculer le nombre de zéros à la fin d'un nombre dans la base b ; c'est . Comme nous calculons en binaire, c'est . Tout ce que nous devons faire est d'appliquer ce code intégré aux nombres de 1 à 2 n -1 inclus.bọ2

Il existe deux façons simples d'itérer sur une plage de nombres dans Jelly, et R, et mes tentatives précédentes sur ce problème en ont utilisé une. Cependant, dans ce cas, il est possible d'aller un peu plus court :, lorsque vous donnez un nombre en entrée, vous permettra de faire une itération qui arrête un élément court (en général, est une fonction intégrée utilisée pour traiter tout sauf un élément de quelque chose). C'est exactement ce que nous voulons dans ce cas (car il 2*génère un trop grand nombre d'éléments), donc l'utiliser pour établir un lien 2*et ọ2entrer 2*Ṗọ2nous donne une solution de 5 octets au problème.


1

Awk, 72 caractères

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)

Le format de sortie est le même que celui de 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

Script bash, 100 96 caractères

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

Le format de sortie est le même que celui de Keith Randall .

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

Modifier : Saved 4 caractères par peter commentaire s.


1
Vous pouvez ajouter des espaces et enregistrer quelques caractères en faisant écho$@
Peter Taylor

@PeterTaylor: Bon point. permettez-moi de le mettre à jour.
Prince John Wesley

1

J, 23 octets

solution de nombres binaires

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

Cette solution utilise la méthode de comptage binaire décrite dans cette vidéo .

C'est-à-dire que je génère les chiffres binaires de 1jusqu'à 2^n, puis prends des infixes de longueur 2 et compare chaque bit au bit correspondant du nombre précédent, et vérifie s'ils sont inégaux. Le nombre de bits inégaux est la sortie de ce mouvement.

Sortie, par exemple, pour 3 disques, où le plus petit disque est étiqueté 1:

1 2 1 3 1 2 1

1 signifie "déplacer le plus petit disque d'une cheville vers la droite, en rebouclant sur la première cheville si nécessaire"

n, pour tous les autres n, signifie "déplacer le disque nvers une cheville légale" (il y en aura toujours exactement un)

Essayez-le en ligne!

solution récursive

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

Même sortie que la solution ci-dessus, mais la logique ici rend la nature récursive du problème plus claire.

Le visualiser sous forme d'arbre souligne également ce point:

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

Essayez-le en ligne!


1
la nature fortuite de soumettre votre réponse plus de 5 ans après que la question initiale a été posée dans la même heure que je suis revenue pour examiner les réponses à cette question que j'ai soumise il y a plus de 5 ans… wow. +1
Carter Pape



0

R , 73 octets

Mettre R sur la carte. Inspiré de [la réponse de Keith Randall] [1] avec une entrée simplifiée, n'imprimez que la fin et le début du rattachement pour économiser 2 octets. Également des chevilles indexées 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)}}

Essayez-le en ligne!


0

JavaScript (ES6), 45b

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

Par exemple, appeler h(4, 'A', 'B', 'C')(déplacer 4 disques du piquet A au piquet C en utilisant le piquet auxiliaire B)

Retour 'ABACBCABCACBABACBCBACABCABACBC' (déplacer un disque du piquet A au piquet B, déplacer un disque du piquet A au piquet C, déplacer un disque du piquet B au piquet C, etc.)


1
Agréable. Je me demande si les paramètres f, a, t devraient avoir des valeurs par défaut incluses dans la définition de la fonction? Sinon, les soumissions pourraient inclure des données arbitraires dans des arguments supplémentaires. Je suis un débutant, donc quelqu'un de plus expérimenté devrait le conseiller.
John Rees
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.