IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

Tour de Hanoi


Sujet :

C

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    6
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 6
    Par défaut Tour de Hanoi
    Bonjour à tous !
    J'ai un problème avec un exercice bien connu en C : le jeu des tours de Hanoi.
    Le principe est simple :
    On dispose de trois socles. L'un de ces socles contient n anneaux, rangés par taille décroissante. Le but est de déplacer ces n anneaux du premier socle (socle A) sur le socle C, en s'aidant d'un socle intermédiaire noté B. Les règles de déplacement des anneaux sont simples : un seul anneau peut-être déplacé à la fois, et un anneau peut être posé que sur un autre anneau de plus grande taille.

    Dans mon exercice, les socles sont matérialisés par des piles. Voici la structure :

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    typedef struct maillon  {
    			   int v;
    			   struct maillon* suiv;
    			}Maillon;
     
    typedef Maillon* Pile;

    On commence par remplir la pile A avec le nombre d'anneaux voulu :

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Pile Remplir (void)
    {
    	int n,i;
    	Pile A;
    	A=pilenouv();
     
    	printf("Entrez le nombre d\'anneaux : ");
    	scanf("%d",&n);
     
    	for (i=1;i<=n;i++)
    	{
    		A=empiler(A,i);
    	}
     
    	return A;
    }

    Ensuite, on a une fonction nommé Hanoi, qui utilise la récursivité pour résoudre le problème :

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    void Hanoi (Pile A, Pile B, Pile C, int n)
    {
    	if (n==1)
    	{
    		C=empiler(C,tete(A));
    		A=depiler(A);
    	}
     
    	else
    	{
    		Hanoi (A,C,B,n-1);
     
    		B=empiler(B,tete(A));
    		A=depiler(A);
     
    		Hanoi (B,A,C,n-1);
    	}
    }

    Voici la main :
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int main (void)
    {
     
    	Pile A,B,C;
     
    	A=Remplir();
    	B=pilenouv();
    	C=pilenouv();
     
    	Hanoi(A,B,C,longueur(A));
     
    	return 0;
     
    }

    Et si besoin est, voici la liste des fonctions appelés par les fonctions Hanoi et Remplir :

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    Pile pilenouv (void)
    {
    	return NULL;
    }
     
    Pile empiler (Pile p, int data)
    {
    	Pile sauv=p;
     
    	p=(Pile)malloc(sizeof(Maillon));
    	if (p==NULL) {printf("Erreur d\'allocation memoire ... !\n");exit(1);}
     
    	p->v=data;
    	p->suiv=sauv;
     
    	return p;
    }
     
    Pile depiler (Pile p)
    {	
    	Pile tete=p;
     
    	if (vide(p)) {printf("On ne peut depiler une pile vide ... \n");exit(1);}
     
    	p=p->suiv;
    	free(tete);
    	return p;
    }
     
    int longueur (Pile p)
    {
    	int l=0;
     
    	while (p!=NULL)
    	{
    		p=p->suiv;
    		++l;
    	}
     
    	return l;
    }

    Premièrement, je ne suis pas du tout sûr que cet algorithme soit bon. Il compile sans soucis, mais je sais pas comment m'y prendre pour qu'à l'écran, on obtienne les différentes étapes. Par exemples, pour n=2, je voudrais que le programme affiche :

    Operation 1 : je prends l'anneau de A et je le mets sur B
    Operation 2 : je prends l'anneau de A et je le mets sur C
    Opération 3 : je prends l'anneau de B et je le mets sur C
    FIN.

    Quelqu'un peut-il m'aider à finir ce programme ?

    Merci d'avance, et n'hésitez pas à demander plus de détails !

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 480
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 480
    Par défaut
    Citation Envoyé par Stonkeep Voir le message
    Bonjour à tous !
    J'ai un problème avec un exercice bien connu en C : le jeu des tours de Hanoi.
    Bonjour. Voici quelques remarques sur la forme et le fond :

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    typedef struct maillon  {
    			   int v;
    			   struct maillon* suiv;
    			}Maillon;
     
    typedef Maillon* Pile;
    C'est correct en soi mais ce n'est que très rarement une bonne idée de masquer le pointeur sur « Maillon » dans un nouveau nom de type. Cependant, beaucoup de cours sont construits comme ça. Je suppose que c'est imposé par ton prof'.

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Pile Remplir (void)
    {
    	int n,i;
    	Pile A;
    	A=pilenouv();
     
    	printf("Entrez le nombre d\'anneaux : ");
    	scanf("%d",&n);
     
    	for (i=1;i<=n;i++)
    	{
    		A=empiler(A,i);
    	}
     
    	return A;
    }
    Attention : en incrémentant i à chaque tour de boucle, tu empiles chaque disque sur un disque plus petit que lui ! Il faut partir de n et décrémenter tant que i est supérieure à zéro.

    D'autre part, il aurait été plus propre de passer la hauteur n de ta pile en tant qu'argument de ta fonction, et laisser le soin à ta fonction main d'interroger l'utilisateur avec scanf.

    Ensuite, on a une fonction nommé Hanoi, qui utilise la récursivité pour résoudre le problème :

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    ...
     
    	else
    	{
    		Hanoi (A,C,B,n-1);
     
    		B=empiler(B,tete(A));
    		A=depiler(A);
     
    		Hanoi (B,A,C,n-1);
    	}
    }
    Ça, en revanche, c'est incorrect. Les opérations d'empilement et de dépilement doivent être identiques à celles du cas précédent. À dire vrai, ce doit en fait être les mêmes ! En général, ce sont les appels à Hanoi() que l'on fait précéder de « if (n>1) ... ».

    L'idée générale étant que lorsque l'on a plus d'un disque à déplacer, on déplace n-1 disques sur le piquet intermédaire, on déplace manuellement le dernier disque sur le piquet d'arrivée, et on rebouge les n-1 disques du piquet intermédiaire vers le piquet d'arrivée. Et comme bouger n-1 disques, c'est exactement le même problème que d'en déplacer n, et bien on rappelle deux fois la fonction hanoi() pour traiter le même problème avec un disque de moins.

    Il n'y a donc pas de raison de faire deux cas parfaitement distincts lorsque n vaut 1 et lorsqu'elle est supérieure.


    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Pile empiler (Pile p, int data)
    {
    	Pile sauv=p;
     
    	p=(Pile)malloc(sizeof(Maillon));
    	if (p==NULL) {printf("Erreur d\'allocation memoire ... !\n");exit(1);}
     
    	p->v=data;
    	p->suiv=sauv;
     
    	return p;
    }
    Pourquoi sauver p dans une variable locale pour l'utiliser ensuite comme une autre variable locale ? Autant directement utiliser celle que tu as déclarée.

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    Pile depiler (Pile p)
    {	
    	Pile tete=p;
     
    	if (vide(p)) {printf("On ne peut depiler une pile vide ... \n");exit(1);}
     
    	p=p->suiv;
    	free(tete);
    	return p;
    }
    Ok, je comprends mieux pourquoi tu utilises tete() : depile renvoie la nouvelle pile. Ok.

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int longueur (Pile p)
    {
    	int l=0;
     
    	while (p!=NULL)
    	{
    		p=p->suiv;
    		++l;
    	}
     
    	return l;
    }
    Premièrement, je ne suis pas du tout sûr que cet algorithme soit bon. Il compile sans soucis, mais je sais pas comment m'y prendre pour qu'à l'écran, on obtienne les différentes étapes. Par exemples, pour n=2, je voudrais que le programme affiche :

    Operation 1 : je prends l'anneau de A et je le mets sur B
    Operation 2 : je prends l'anneau de A et je le mets sur C
    Opération 3 : je prends l'anneau de B et je le mets sur C
    FIN.
    Comme dit plus haut, en principe, ton opération de déplacement ne doit avoir lieu qu'à un seul endroit. Il suffit de rajouter un printf() à cet endroit précis pour suivre automatiquement le déroulement de la séquence.

    Bon courage.

  3. #3
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Bonjour,
    L'expérience m'a montré que manipuler une liste chaînée directement via un pointeur de maillon n'était pas une bonne idée. Je conseillerais plutôt ceci:
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    typedef struct maillon  {
    	int v;
    	struct maillon* suiv;
    } Maillon;
     
    typedef struct pile {
    	struct maillon* sommet;
    } Pile;
    Ensuite, toutes tes fonctions qui touchent à la pile prennent un pointeur Pile* en paramètre.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

Discussions similaires

  1. Tour de Hanoi
    Par David Fleury dans le forum Algorithmes et structures de données
    Réponses: 24
    Dernier message: 09/06/2007, 17h59
  2. Réponses: 13
    Dernier message: 11/12/2006, 14h44
  3. Tours de Hanoi
    Par leakim56 dans le forum C
    Réponses: 11
    Dernier message: 23/06/2006, 13h02
  4. Tour de Hanoi
    Par issou dans le forum C
    Réponses: 9
    Dernier message: 22/10/2005, 19h43

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo