Probleme – Le jeu de soustraction, et sa périodicité
Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 27 — Jeux, stratégies et recherche heuristique
Énoncé
On généralise l'exercice 27.2 : l'ensemble des retraits autorisés est un ensemble fini , et celui qui prend la dernière allumette gagne.
- Écrire le calcul des positions perdantes, sans passer par le graphe biparti.
- Démontrer que pour les positions perdantes sont les multiples de .
- Montrer que pour tout fini, la suite des positions perdantes est périodique à partir d'un certain rang.
- Vérifier sur et .
Corrigé
1. Le calcul direct. Le graphe biparti est utile pour modéliser, mais la position ne dépend pas du joueur : le jeu est impartial. On calcule donc un seul tableau.
(* gagnante.(n) vaut true si le joueur qui doit jouer avec n allumettes gagne.
Précondition : s est une liste d'entiers >= 1, nmax >= 0.
INVARIANT : après le tour n de la boucle, gagnante.(0..n) est correct.
Complexité : Theta(nmax · |s|). *)
let positions s nmax =
let gagnante = Array.make (nmax + 1) false in
for n = 1 to nmax do
gagnante.(n) <-
List.exists (fun k -> k <= n && not gagnante.(n - k)) s
done;
gagnante
La ligne qui porte tout le jeu est not gagnante.(n - k) : une position est gagnante s'il existe un coup menant à une position perdante pour l'adversaire. C'est le du chapitre, et le y est caché dans la négation — une position est perdante quand tous ses coups mènent à des positions gagnantes. Terminaison : boucle bornée. Correction : par récurrence forte sur , chaque valeur ne dépend que de valeurs d'indice strictement inférieur, déjà calculées.
2. Le cas . Montrons par récurrence forte que est perdante si et seulement si .
- : le joueur ne peut pas jouer, il a perdu. Et .
- Si et : tout coup mène à , qui vérifie puisque . Par hypothèse de récurrence, toutes ces positions sont gagnantes : est perdante.
- Si : posons , qui vérifie . Le coup est légal et mène à , multiple de , donc perdante : est gagnante.
La stratégie s'en déduit : retirer , une fonction de la seule position courante — sans mémoire.
3. La périodicité, pour tout fini. Soit . La ligne de code ci-dessus montre que ne dépend que de , c'est-à-dire de la fenêtre des valeurs précédentes. Une telle fenêtre est un mot de : il n'y en a que , donc un nombre fini.
En parcourant , on lit une suite de fenêtres ; par le principe des tiroirs, deux d'entre elles coïncident au plus tard au rang . Or la fenêtre détermine entièrement toute la suite qui la suit. Deux fenêtres égales aux rangs engendrent donc deux suites identiques : la suite est périodique de période à partir de . La borne est grossière — les périodes observées sont très petites — mais elle suffit à établir l'existence.
4. Les vérifications, mesurées.
| positions perdantes | lecture | |
|---|---|---|
| ou | ||
| ou |
Pour , la période est et non : la formule « multiples de » ne se généralise pas.
Et réserve un piège, celui de l'exercice 27.9. La position y est perdante sans être nulle : le joueur ne peut rien retirer, puisque le plus petit retrait vaut , et un joueur qui ne peut pas jouer a perdu. Or si l'on calcule ces positions par un attracteur sur le graphe biparti , avec le code du cours, on obtient une réponse fausse — ou au lieu de ou — parce que l'état est un cul-de-sac de que ce code n'ajoute jamais à l'attracteur. Le défaut de l'exercice 27.9 n'est pas une curiosité : il change la réponse du premier jeu venu. La version directe ci-dessus n'y est pas sujette, car sa disjonction List.exists sur une liste de coups vide vaut false, ce qui est exactement « le joueur bloqué perd ».
C'est le genre de cas limite qu'un jeu de tests doit contenir, et le chapitre chap:discipline explique pourquoi : il est aux frontières du domaine.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.