Récursivité et struct craquant 1 Tours de Hanoï 2 Conjecture

Transcription

Récursivité et struct craquant 1 Tours de Hanoï 2 Conjecture
Sup Galilée
Année 2015-2016
Algorithmique
CP2I2
TP 1
Récursivité et struct craquant
1
Tours de Hanoï
Le problème des tours de Hanoï est un jeu de réflexion imaginé par le mathématicien français
Édouard Lucas, et consistant à déplacer des disques de diamètres différents d’une tour A à une
tour C en passant par une tour B, et ceci en un minimum de coups, tout en respectant les règles
suivantes :
– on ne peut déplacer plus d’un disque à la fois,
– on ne peut placer un disque que sur un autre disque plus grand que lui ou sur un emplacement vide.
On suppose que cette dernière règle est également respectée dans la configuration de départ.
1.1
Fonction Hanoï
Écrire une fonction récursive hanoi qui résoud le problème des tours de Hanoï en utilisant le
fait que déplacer n disques de la tour A à la tour C en utilisant la tour B, revient à :
– déplacer les n − 1 premiers disques de la tour A à la tour B en utilisant la tour C ;
– déplacer le neme disque de la tour A à la tour C ;
– déplacer les n − 1 disques de la tour B à la tour C en utilisant la tour A ;
1.2
Main
Écrire un programme qui demande à l’utilisateur le nombre de disques n et qui appelle la
fonction hanoi qui résoud le problème des tours de Hanoï.
2
Conjecture de Syracuse
Soit la fonction mathématique f : N → N définie par :
(
x/2 si x pair
f : x 7→
3x + 1 sinon.
On appelle cette fonction la fonction de Collatz et comme vous pourrez le vérifier sur internet
elle a donné et donne toujours du fil à retordre aux mathématiciens. Voici pourquoi.
Soit un entier positif x. Si on calcule f (x), puis f (f (x)), puis f (f (f (x))) etc. on finit toujours
par tomber sur la valeur 1 quelle que soit la valeur de x ∈ N∗ . Aucun mathématicien n’est arrivé
à ce jour à le démontrer (et on ne sait même pas démontrer que c’est indécidable, c’est à dire
non démontrable à l’aide des mathématiques usuelles). Cela reste une conjecture.
On peut également formuler le problème avec une suite définie par récurrence. On prend u0
égal à x, et un+1 = f (un ). Les termes successifs de la suite sont alors : x, f (x), f (f (x)) etc.
La conjecture est que la suite (un ) finit par atteindre la valeur 1, pour tout u0 = x ∈ N∗ . Si on
poursuit le calcul après avoir trouvé une première fois 1, la suite devient périodique, de période
4, 2, 1. On dit que la suite est entrée dans un puit. Par exemple en partant de 15 on a la suite :
15, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1, (4, 2, 1, 4, 2, 1, . . .)
Les mathématicien parlent de la trajectoire de x pour désigner la suite des valeurs prises par
(un ) avant d’atteindre le premier 1 lorsque la suite part de u0 = x. Ils définissent également le
1
temps de vol comme étant le nombre de termes avant le premier 1 (ici 17) et ils désignent par
altitude maximale, la valeur maximale prise par la suite (ici 160).
1. Écrire en langage C une fonction Collatz calculant la valeur de la fonction f sur son argument.
2. Écrire une procédure (un fonction ne renvoyant pas de résultat) récursive Syracuse qui
calcule les itérations successives de la fonction de Collatz sur un entier donné en argument,
jusqu’à trouver 1. Pour le moment la fonction Syracuse n’affichera rien et ne renverra pas
de résultat (mais elle doit calculer chacun des termes successifs de Collatz !). Si vous ne
trouvez pas, vous pouvez écrire une procédure non récursive qui utilise plus classiquement
un while pour calculer tour à tour toutes les valeurs de la suite.
3. Ajouter ensuite un affichage des valeurs successives trouvées dans cette fonction. Dans
l’ordre direct. Puis dans l’ordre inverse. Comment feriez-vous pour obtenir l’ordre direct
avec un while ? et l’ordre inverse ?
4. En reprenant le code de Syracuse écrire une fonction récursive temps_de_vol qui renvoie
le nombre d’itérations de la fonction de Collatz nécessaire pour atteindre 1.
5. Comment faire en sorte que le temps de vol soit affiché plutôt que renvoyé comme valeur
de retour ?
6. En reprenant le code précédant écrire une nouvelle fonction altitude qui renvoie la valeur
maximum prise par les termes successifs de l’itération (x compris).
7. Ajouter un argument booléen à votre procédure Syracuse qui déterminera s’il faut afficher
ou non les termes de la suite. Exemple (avec le booléen à faux) :
Départ 15 : temps de vol 17, altitude maximale 160
8. Écrire une fonction conjecture qui exécute tour à tour la procédure Syracuse sur les entiers 2 à n, pour n donné en argument, sans afficher les termes de la suite.
Tester
Depart
Depart
Depart
de 2 a 4
2 : temps de vol 1, altitude maximale 2
3 : temps de vol 7, altitude maximale 16
4 : temps de vol 2, altitude maximale 4
9. Ajouter un menu à votre programme proposant d’afficher les termes de la suite pour une
valeur saisie par l’utilisateur ou de tester la conjecture jusqu’à une valeur saisie par l’utilisateur. Pensez à réutiliser des fonctions que vous avez déjà écrites en TP !
10. Ajouter à votre menu la possibilité de tester la conjecture sur tous les entiers indéfiniement
(ctrl-C pour arrêter).
3
Le jeu de la tablette de chocolat
Une tablette (ou plaquette) de chocolat est une matrice de carreaux de chocolat que l’ont
peut couper selon les lignes ou les colonnes qui séparent les carreaux. La hauteur et la largeur
se comptent en nombre de carreaux. On a peint en vert un carreau dans le coin d’une tablette.
Deux joueurs coupent tour à tour la tablette, au choix, selon une ligne ou une colonne (pas nécessairement près du bord). L’objectif est de ne pas être le joueur qui se retrouve avec le carreau
vert (la tablette de hauteur 1 et de largeur 1).
Nous allons programmer ce jeu en faisant jouer à l’ordinateur le rôle de l’opposant.
3.1
Type utilisateur
Définir un type utilisateur qui servira à représenter la tablette (faire simple !).
2
3.2
Le tour de jeu et l’arbitre
Le joueur commencera la partie, puis l’opposant (l’ordinateur) jouera et ainsi de suite. À
chaque fois l’une des deux dimensions de la tablette sera modifiée, jusqu’à ce qu’il y ait un
perdant. Vous adopterez la structure suivante pour le programme :
– Une variable sera utilisée pour déterminer à qui vient le tour de jouer (joueur ou opposant) ;
– Une boucle réalisera le tour de jeu ;
– Une fonction partie_perdue() prenant en argument la tablette et renvoyant vrai lorsque
celle-ci se réduit au carré vert, servira à construire la condition de sortie de la boucle ;
– Après quoi un affichage annoncera au joueur s’il a gagné ou perdu.
– À l’intérieur de la boucle, selon le tour, il sera fait appel à une fonction joueur() ou bien à
une fonction opposant() (ces fonctions doivent modifier la tablette).
Vous êtes libre du choix de la taille initiale de la tablette.
3.3
Affichage
Réaliser une fonction d’affichage de la tablette qui sera appelée au début de chaque tour
de jeu. Vous pouvez dans un premier temps vous contenter d’un affichage numérique de ses
dimensions, et par la suite améliorer votre programme avec un affichage plus graphique.
3.4
Les joueurs
Le joueur étant l’humain entre le clavier et l’écran, il faudra lui faire saisir le choix de son
coup, en deux temps : couper des colonnes ou couper des lignes ? Combien ? À vous de déterminer
la manière de réaliser cette saisie.
Vous pouvez commencer à tester le fonctionnement de votre programme en utilisant la fonction joueur() également pour l’opposant.
L’opposant jouera au hasard. Pour cela vous pouvez vous aider d’une fonction nombre_aleatoire
qui tire au hasard un nombre entre zéro et son argument (un entier positif). Cette fonction utilisera rand. N’oubliez pas d’initialiser le générateur de nombres aléatoires dans le main() ni
d’inclure les bons fichiers d’en-tête (fonctionnalités supplémentaires).
Tester votre programme. Avez-vous prévu les cas où l’un des deux joueurs n’a pas le choix ?
Est-ce que les coups invalides sont rendus impossibles ? Vous pouvez réutiliser et améliorer des
fonctions vues précédemment (choix_utilisateur(), par exemple).
4
4.1
Améliorations
Tablette aléatoire
Dans votre programme la tablette est initialisée au départ. Pouvez-vous utiliser une fonction
sans argument, tablette_aleatoire qui renvoie une tablette aléatoire ?
4.2
L’opposant parfait
Pouvez-vous trouver la meilleure manière de jouer (cela dépend de la tablette de départ) ? Si
oui, faîtes en profiter votre opposant. Vous pouvez également le faire se tromper de temps en
temps, en introduisant pour cela une dose de hasard...
4.3
Plusieurs tablettes
On peut utiliser plusieurs tablettes pour ce jeu : chaque joueur choisit sur quelle tablette jouer
son tour et le perdant sera celui recevant pour son tour toutes les tablettes à la dimension 1 × 1.
3