Exercices#
Calculabilité et décidabilité
Usage de l’IA, indiqué sur chaque exercice : aucune ; en appui (déboguer, reformuler, vérifier ; la réponse finale est écrite et comprise par vous) ; l’exercice s’appuie sur une réponse d’IA. Voir la charte d’usage de l’IA.
Chapitre d’idées : la plupart des exercices se rédigent en français. Soignez les raisonnements. Niveau : ★ échauffement, ★★ classique (niveau bac), ★★★ défi ; les coups de pouce et les corrigés se déplient sous chaque exercice.
Un programme est une donnée#
★ Exercice 1 — Qui est la machine, qui est la donnée ? #
• essentiel
Pour chacune des situations, préciser quel est le programme (la machine) et quelle est sa donnée d’entrée.
-
Dans un terminal, on tape
python3 tri.py. -
Le compilateur
gcctraduit un fichierjeu.cen exécutable. -
Un antivirus analyse le fichier
photo.exe. -
Un éditeur de texte colore la syntaxe d’un fichier
code.py.
Coup de pouce
Le programme est celui que l’on lance, celui qui « travaille » ; la donnée est ce qu’il reçoit, puis lit ou transforme. Une fois les quatre réponses écrites, que remarque-t-on sur la nature des données ?
Corrigé
-
Machine :
python3. Donnée : le fichiertri.py. -
Machine :
gcc. Donnée : le fichierjeu.c. -
Machine : l’antivirus. Donnée : le fichier
photo.exe. -
Machine : l’éditeur de texte. Donnée : le fichier
code.py.
Dans chaque cas, la « donnée » est un programme : un programme peut donc être avalé par un autre.
★ Exercice 2 — Un programme n’est qu’une chaîne de caractères #
En Python, on peut écrire un programme dans une chaîne à l’aide des triples guillemets :
programme = """
x = 10
while x > 0:
x = x - 1
"""
-
Expliquer, en une ou deux phrases, pourquoi tout programme Python peut être vu comme une chaîne de caractères.
-
En déduire pourquoi il n’y a rien d’absurde à donner un programme en argument à un autre programme — ni même à un programme de se prendre lui-même en argument.
Coup de pouce
De quoi est fait le code source d’un programme quand on l’ouvre dans un éditeur ? Et que peut faire un programme d’une chaîne de caractères qu’il reçoit ?
Corrigé
-
Un programme est écrit avec des caractères (lettres, chiffres, symboles, retours à la ligne) : son code source est une chaîne de caractères, que l’on peut stocker dans une variable, dans un fichier, transmettre, etc.
-
Puisqu’un programme est une simple donnée (une chaîne), rien n’empêche de la passer en argument à un autre programme (un interpréteur, un compilateur…) — ni de passer le code d’un programme à ce programme lui-même : c’est ce qui rendra possible le raisonnement du problème de l’arrêt.
Décidable ou indécidable ?#
★ Exercice 3 — Tri sélectif #
• essentiel
Pour chaque problème de décision, dire s’il est décidable ou indécidable. Justifier d’un mot.
-
« L’entier \(n\) est-il premier ? »
-
« La liste
Lest-elle triée dans l’ordre croissant ? » -
« Le programme
P, lancé sur l’entréex, va-t-il s’arrêter ? » -
« Le mot
mfigure-t-il dans ce dictionnaire de \(100\,000\) mots ? » -
« Les deux programmes
PetQcalculent-ils exactement la même fonction ? »
Coup de pouce
Un problème est décidable s’il existe un algorithme qui répond, en un temps fini, pour toute entrée. Chercher pour chacun un tel algorithme (un parcours fini, des divisions à tester…) ; quand la question porte sur le comportement d’un programme quelconque, se rappeler les résultats du cours.
Corrigé
-
Décidable (tester les diviseurs ; l’algorithme s’arrête toujours).
-
Décidable (parcourir la liste une fois).
-
Indécidable : c’est le problème de l’arrêt.
-
Décidable (rechercher le mot ; le dictionnaire est fini).
-
Indécidable : décider si deux programmes calculent la même fonction est une question sur leur comportement (théorème de Rice).
▷ Erreur fréquente. Répondre « décidable : il suffit d’exécuter P et Q et de comparer leurs résultats » : il y a une infinité d’entrées, et une exécution peut ne jamais s’arrêter.
★★ Exercice 4 — Un décideur qui s’arrête toujours #
On veut décider le problème « \(n\) est-il premier ? » pour un entier \(n \ge 2\).
-
Recopier et compléter cette fonction :
Coup de pouce
Q1 : un diviseur \(d\) de \(n\), autre que \(1\) et \(n\), est compris entre \(2\) et \(n-1\). Dès qu’on en trouve un, peut-on conclure ?
def est_premier(n): for d in range(2, ...): # (a) diviseurs a tester if n % d == 0: return ... # (b) return ... # (c) -
Expliquer pourquoi cette fonction s’arrête toujours, quelle que soit l’entrée. Que peut-on en conclure sur la décidabilité du problème ?
Coup de pouce
Q2 : combien de tours la boucle
forfait-elle au plus ? Peut-elle tourner indéfiniment ?
Corrigé
-
Démarche : on teste tous les diviseurs possibles, de \(2\) à \(n - 1\) ; dès qu’on en trouve un, on peut conclure « pas premier » ; on ne conclut « premier » qu’après la boucle.
def est_premier(n): for d in range(2, n): # (a) on teste 2, 3, ..., n-1 if n % d == 0: return False # (b) un diviseur -> pas premier return True # (c) aucun diviseur -> premier▷ Erreur fréquente. Placer
return Truedans la boucle (dans unelse) : la fonction conclurait « premier » dès le premier entier testé qui ne divise pas \(n\) (est_premier(9)renverraitTrue). -
La boucle
forparcourt un intervalle fini (\(2\) à \(n-1\)) : elle se termine forcément, donc la fonction s’arrête toujours et renvoie la bonne réponse. Le problème « \(n\) est-il premier ? » est donc décidable.
Le problème de l’arrêt#
★★ Exercice 5 — Va-t-il s’arrêter ? #
• essentiel
Pour chaque appel, dire s’il s’arrête ou non, et le justifier brièvement.
def compte(n): def mystere(n):
while n != 0: while n != 1:
n = n - 1 if n % 2 == 0:
return "fini" n = n // 2
else:
n = 3 * n + 1
return "fini"
-
compte(5)puiscompte(-3).Coup de pouce
Pour
compte(-3), écrire les premières valeurs den: se rapproche-t-on de \(0\) ? -
mystere(6). Dérouler les valeurs successives den. -
Culture : personne au monde ne sait aujourd’hui si
mystere(n)s’arrête pour tout entier \(n \ge 1\) (conjecture de Syracuse). Que cela illustre-t-il sur la difficulté de prédire l’arrêt d’un programme ?
Corrigé
-
compte(5):nvaut \(5,4,3,2,1,0\) \(\rightarrow\) s’arrête (renvoie"fini").compte(-3):nvaut \(-3,-4,-5,\dots\) et ne vaut jamais \(0\) \(\rightarrow\) ne s’arrête pas.▷ Erreur fréquente. Conclure que
compte(-3)s’arrête parce quendiminue :ns’éloigne de \(0\). Un variant doit être un entier positif qui décroît vers la condition d’arrêt. -
mystere(6):nvaut \(6,3,10,5,16,8,4,2,1\) \(\rightarrow\) s’arrête. -
On a là un programme d’une extrême simplicité dont personne ne sait s’il s’arrête pour toute entrée. C’est une illustration frappante : prédire l’arrêt d’un programme peut être arbitrairement difficile — et le théorème de l’arrêt dit que, dans le cas général, aucune méthode automatique ne peut le faire.
★★ Exercice 6 — Refaire la preuve du problème de l’arrêt #
• essentiel
On suppose disposer d’une fonction halt(prog, x) renvoyant True si prog(x) s’arrête, False sinon. On écrit alors :
def paradoxe(prog):
if halt(prog, prog):
while True:
pass
else:
return "je m'arrete"
-
On lance
paradoxe(paradoxe). Cas 1 : on suppose quehalt(paradoxe, paradoxe)renvoieTrue. Quelle branche duifest exécutée ? L’appel s’arrête-t-il ? En quoi est-ce contradictoire ?Coup de pouce
Suivre le code ligne à ligne : quelle instruction est exécutée quand le test du
ifest vrai ? Comparer ensuite avec ce quehaltavait prédit. -
Cas 2 : on suppose que
halt(paradoxe, paradoxe)renvoieFalse. Reprendre le même raisonnement. -
Conclure : que vient-on de démontrer au sujet de
halt?
Corrigé
-
Cas 1 :
halt(paradoxe, paradoxe)vautTrue, donc le test duifest vrai : on exécutewhile True\(\rightarrow\) l’appel ne s’arrête pas. Orhaltavait annoncé qu’il s’arrêtait : contradiction.▷ Erreur fréquente. S’arrêter à « on entre dans
while True» : il faut encore confronter ce comportement à la prédiction dehalt, qui annonçait l’arrêt, pour obtenir la contradiction. -
Cas 2 :
halt(paradoxe, paradoxe)vautFalse, donc le test est faux : on exécute leelse, qui faitreturn\(\rightarrow\) l’appel s’arrête. Orhaltavait annoncé qu’il ne s’arrêtait pas : contradiction. -
Dans les deux cas,
haltse trompe : la fonctionhaltne peut pas exister. Le problème de l’arrêt est indécidable (Turing, 1936).
★★ Exercice 7 — Cousins du paradoxe #
Expliquer en quoi les deux énoncés suivants « tournent en rond », et quel lien ils ont avec le programme paradoxe de l’exercice précédent.
-
« Cette phrase est fausse. »
-
« Le barbier du village rase exactement ceux qui ne se rasent pas eux-mêmes. Rase-t-il le barbier ? »
Coup de pouce
Supposer la phrase vraie, puis fausse : que se passe-t-il dans chaque cas ? Même démarche pour le barbier : il se rase, puis il ne se rase pas.
Corrigé
-
« Cette phrase est fausse » : si on la suppose vraie, elle affirme sa propre fausseté (donc fausse) ; si on la suppose fausse, alors ce qu’elle dit est faux, donc elle est vraie. Aucune valeur de vérité ne tient.
-
Le barbier : s’il se rase lui-même, il fait partie de ceux qui se rasent, donc il ne devrait pas se raser ; s’il ne se rase pas, il devrait. Impasse identique.
Lien : dans les deux cas, un objet parle de lui-même et se retourne contre sa propre affirmation. C’est exactement le ressort de paradoxe(paradoxe), qui fait le contraire de ce que halt prédit de lui : l’auto-référence.
★★★ Exercice 8 — La contagion de l’indécidable #
≈ 15 min
On admet le théorème de l’arrêt (indécidable). On voudrait maintenant un programme renvoie42(prog, x) qui dirait à coup sûr si prog(x) finit par renvoyer la valeur \(42\).
-
Expliquer pourquoi, si un tel programme existait, il « en saurait trop » sur le comportement de
prog.Coup de pouce
Penser à un programme qui ferait un très long calcul avant, peut-être, de renvoyer \(42\).
Coup de pouce 2 (début de solution)
Partir d’un programme
progquelconque et fabriquerprog2(x)qui exécute d’abordprog(x), puis renvoie \(42\). Quandprog2(x)renvoie-t-il \(42\) ? -
Comment appelle-t-on le résultat général affirmant que « toute question non triviale sur le comportement d’un programme est indécidable » ?
Corrigé
-
On raisonne par l’absurde : on suppose que
renvoie42existe, et on montre qu’il permettrait alors de décider l’arrêt. Soitprogun programme quelconque. On fabrique le programmeprog2suivant.def prog2(x): prog(x) # on execute prog sur x, en ignorant son resultat return 42Si
prog(x)s’arrête,prog2(x)passe à la ligne suivante et renvoie \(42\) ; siprog(x)ne s’arrête pas,prog2(x)ne renvoie jamais rien. Doncprog2(x)renvoie \(42\) si et seulement siprog(x)s’arrête. On aurait alorshalt(prog, x) = renvoie42(prog2, x):renvoie42déciderait le problème de l’arrêt, ce qui est impossible (théorème de l’arrêt). Doncrenvoie42n’existe pas : la question «prog(x)renvoie-t-il \(42\) ? » est, elle aussi, indécidable. On dit qu’on a réduit (ramené) le problème de l’arrêt à cette question. -
C’est le théorème de Rice.
Calculabilité et Turing-complétude#
★ Exercice 9 — Vrai ou faux #
• essentiel
Répondre vrai ou faux et justifier d’un mot.
-
Le langage C est « plus puissant » que Python : il peut calculer des fonctions que Python ne peut pas.
-
Une fonction calculable est une fonction pour laquelle il existe un algorithme qui la calcule en un temps fini.
-
La thèse de Church-Turing est un théorème que l’on peut démontrer.
-
Puisque le langage Brainfuck n’a que \(8\) instructions, il est moins puissant que Python.
-
Le problème de l’arrêt serait décidable si on l’écrivait en C plutôt qu’en Python.
Coup de pouce
Relire dans le cours les définitions de fonction calculable, de la thèse de Church-Turing et de la Turing-complétude. Pour les affirmations 1, 4 et 5, se demander si elles comparent des langages Turing-complets.
Corrigé
-
Faux : C et Python sont tous deux Turing-complets, ils calculent les mêmes fonctions.
-
Vrai : c’est la définition d’une fonction calculable.
-
Faux : la thèse de Church-Turing n’est pas un théorème (elle relie l’intuition de « calcul » à un modèle formel) ; elle est admise, jamais démentie.
▷ Erreur fréquente. Parler du « théorème » de Church-Turing : une notion intuitive (« calculer ») ne peut pas entrer dans une démonstration ; la thèse est admise parce qu’elle n’a jamais été démentie.
-
Faux : le nombre d’instructions ne fait pas la puissance ; Brainfuck est Turing-complet, donc aussi puissant que Python (juste beaucoup moins commode).
-
Faux : l’indécidabilité ne dépend pas du langage ; le problème de l’arrêt est indécidable dans tout langage Turing-complet.
P, NP et le million de dollars au-delà du programme#
★ Exercice 10 — Vérifier, est-ce facile ? #
Pour chaque problème, dire si vérifier une solution qu’on vous propose est facile (rapide), et si trouver une solution semble facile ou difficile.
-
Trier une liste de \(1000\) nombres.
-
Une grille de Sudoku (\(9\times 9\)).
-
« Existe-t-il une tournée passant par ces \(30\) villes, de longueur \(\le 500\) km ? »
Coup de pouce
Pour chaque problème, distinguer deux tâches : contrôler une solution qu’on vous donne, ou la fabriquer soi-même. Pour fabriquer, combien de possibilités faudrait-il essayer ?
Corrigé
-
Trier : trouver est facile (algorithme en \(n\log n\)) et vérifier aussi. Problème de classe P.
-
Sudoku : vérifier une grille remplie est facile ; trouver le remplissage semble difficile.
-
Voyageur de commerce : vérifier qu’une tournée proposée mesure \(\le 500\) est facile (on additionne) ; trouver la meilleure semble très difficile.
★★ Exercice 11 — Colorier un graphe #
On veut colorier les sommets du graphe ci-dessous avec \(3\) couleurs, sans que deux sommets reliés aient la même couleur.
-
On vous propose le coloriage : A rouge, B vert, C bleu, D rouge, E rouge. Vérifier s’il est correct (parcourir les arêtes). Ce travail de vérification est-il long ?
-
Combien y a-t-il de façons d’attribuer une couleur (parmi \(3\)) aux \(5\) sommets, si on les essaie « bêtement » toutes ? Et pour un graphe à \(n\) sommets ?
Coup de pouce
Chaque sommet a \(3\) choix, indépendamment des autres : combien de choix pour \(2\) sommets ? pour \(3\) ?
-
À quelle classe (P ou NP) ce type de problème appartient-il, et pourquoi ?
Corrigé
-
On parcourt les arêtes : A–B (rouge/vert ok), A–C (rouge/bleu ok), B–C (vert/bleu ok), B–D (vert/rouge ok), C–D (bleu/rouge ok), B–E (vert/rouge ok), D–E (rouge/rouge !). Deux voisins D et E sont tous deux rouges : le coloriage proposé est incorrect. La vérification est rapide (une passe sur les arêtes).
-
Chaque sommet reçoit \(1\) couleur parmi \(3\) : \(3^5 = 243\) possibilités pour \(5\) sommets, et \(3^n\) pour \(n\) sommets — une croissance exponentielle.
-
C’est un problème de NP : on vérifie une solution proposée en temps polynomial (une passe sur les arêtes), mais trouver une \(3\)-coloration semble exiger un temps exponentiel (le problème est même NP-complet).
★★ Exercice 12 — Et si P \(=\) NP ? #
Un mathématicien annonce un algorithme polynomial qui résout le problème SAT (donc P \(=\) NP).
-
Rappeler ce que signifient P, NP, et pourquoi on a toujours P \(\subseteq\) NP.
-
Citer deux conséquences majeures de P \(=\) NP (une pour la sécurité informatique, une pour l’idée de « créativité »).
Coup de pouce
Casser une clé ou trouver une preuve mathématique : est-ce difficile à trouver, facile à vérifier ? Que changerait P \(=\) NP ?
-
La plupart des chercheurs pensent-ils que P \(=\) NP ou que P \(\ne\) NP ? Ce point est-il démontré ?
Corrigé
-
P : problèmes résolubles en temps polynomial. NP : problèmes dont une solution oui se vérifie en temps polynomial. On a P \(\subseteq\) NP car, si l’on sait résoudre vite, on sait vérifier vite (résoudre puis comparer).
-
Conséquences de P \(=\) NP : (sécurité) la cryptographie RSA s’effondrerait, car factoriser un grand nombre deviendrait rapide ; (créativité) trouver deviendrait aussi facile que vérifier — inventer une preuve, une mélodie, une solution ne serait plus « plus dur » que la reconnaître.
-
La quasi-totalité des chercheurs pense que P \(\ne\) NP, mais ce n’est pas démontré : c’est un problème ouvert (l’un des sept problèmes du millénaire, doté d’un million de dollars).
Vers le bac#
★★ Exercice 13 — Terminaison et problème de l’arrêt (d’après Métropole septembre 2024, jour 2) #
• essentiel ≈ 45 min
On dit qu’un appel f(x) termine lorsque son évaluation renvoie une valeur au bout d’un nombre fini d’étapes.
Partie A — boucle while.
def f1(n):
i = n
while i != 10:
i = i + 1
return i
-
Donner les valeurs successives de
ilors def1(7), et dire sif1(7)termine. -
L’appel
f1(-2)termine-t-il ? Si oui, quelle valeur renvoie-t-il ? -
Donner les \(5\) premières valeurs de
ilors def1(12), et dire si l’appel termine. -
Préciser pour quels entiers
nl’appelf1(n)termine.Coup de pouce
Q4 : distinguer \(n \le 10\) et \(n > 10\) : dans quel cas
is’éloigne-t-il de \(10\) ?
Partie B — fonction récursive.
def f2(n):
if n == 0:
return 0
else:
return n + f2(n - 2)
-
f2(4)termine-t-il ? Si oui, quelle valeur renvoie-t-il ? -
f2(5)termine-t-il ? Justifier. -
Déterminer l’ensemble des entiers naturels
npour lesquelsf2(n)termine.Coup de pouce
Q7 :
ndiminue de \(2\) à chaque appel : pour quelsnatteint-on la condition d’arrêtn == 0? -
Écrire une fonction récursive
infinitelle queinfini(n)ne termine pour aucun entiern.Coup de pouce
Q8 : que se passe-t-il pour une fonction récursive sans aucun cas de base ?
Partie C — le problème de l’arrêt. On suppose disposer d’une fonction arret(code_f, x) qui renvoie True si f(x) termine et False sinon. On écrit :
def paradoxe(x):
if arret(x, x):
infini(42)
else:
return 0
On dispose d’une variable code_paradoxe contenant le code de paradoxe, et on s’intéresse à paradoxe(code_paradoxe).
-
Si
arret(code_paradoxe, code_paradoxe)renvoieTrue: quelle instruction s’exécute ensuite ? L’appelparadoxe(code_paradoxe)termine-t-il ? -
Même question si
arret(...)renvoieFalse. -
En déduire qu’une telle fonction
arretne peut pas exister.
Corrigé
Barème indicatif (sur 6) : Q1 : 0,5 ; Q2 : 0,5 ; Q3 : 0,5 ; Q4 : 0,5 ; Q5 : 0,5 ; Q6 : 0,5 ; Q7 : 0,75 ; Q8 : 0,5 ; Q9 : 0,5 ; Q10 : 0,5 ; Q11 : 0,75
Partie A.
-
f1(7):ivaut \(7, 8, 9, 10\) \(\rightarrow\) lewhiles’arrête, f1(7) termine et renvoie \(10\). -
f1(-2):ivaut \(-2, -1, 0, \dots, 10\) \(\rightarrow\) termine, renvoie \(10\). -
f1(12):ivaut \(12, 13, 14, 15, 16, \dots\) (les \(5\) premières : \(12,13,14,15,16\)) ;ine vaut jamais \(10\) \(\rightarrow\) ne termine pas. -
f1(n)termine si et seulement si \(n \le 10\) (on peut alors atteindre \(10\) en incrémentant).
Partie B.
-
f2(4): \(4 + f2(2) = 4 + 2 + f2(0) = 4+2+0 = 6\) \(\rightarrow\) termine, renvoie \(6\). -
f2(5): \(5 + f2(3) = 5+3+f2(1) = 5+3+1+f2(-1) = \dots\) : on décroît de \(2\) à partir d’un impair, on saute \(0\) etndevient négatif sans jamais valoir \(0\) \(\rightarrow\) ne termine pas.▷ Erreur fréquente. Répondre que
f2(5)renvoie \(5 + 3 + 1 = 9\) : après \(1\), l’appel suivant porte sur \(-1\), et la condition d’arrêtn == 0n’est jamais atteinte (en pratique, Python s’interrompt sur uneRecursionError). -
f2(n)termine (pour \(n\) entier naturel) si et seulement si \(n\) est pair. -
Démarche : une fonction récursive sans aucune condition d’arrêt, dont l’appel récursif ne se rapproche de rien.
def infini(n): return infini(n) # appel recursif sans condition d'arret(aucune valeur de
nne fait terminer l’appel.)
Partie C.
-
Si
arret(code_paradoxe, code_paradoxe)renvoieTrue: leifest vrai, on exécuteinfini(42)\(\rightarrow\) l’appelparadoxe(code_paradoxe)ne termine pas. Orarretaffirmait qu’il terminait : contradiction. -
Si
arret(...)renvoieFalse: leifest faux, on exécutereturn 0\(\rightarrow\) l’appel termine. Orarretaffirmait qu’il ne terminait pas : contradiction. -
Dans les deux cas,
arretdonne une réponse démentie par les faits : une telle fonctionarretne peut pas exister.
★★★ Exercice 14 — Un programme comme chaîne de caractères (d’après Asie 2025, jour 1) #
≈ 45 min
En Python, exec(chaine) exécute le programme contenu dans la chaîne. On définit :
programme1 = """
x = 10
y = 10
while x > 0:
x = x - 1
y = y + 1
"""
-
Donner les valeurs de
xetyaprèsexec(programme1). -
Expliquer pourquoi tout programme Python peut être vu comme une chaîne de caractères.
-
On considère :
programme3 = "x = 10\nwhile x != 0:\n x = x - 2" programme4 = "x = 10\nwhile x > 0:\n x = x + 2" programme5 = "x = 10\nwhile x < 0:\n x = x + 4" programme6 = "x = 10\nwhile x != 0:\n x = x - 4"Pour chacun, dire si
exectermine ou non, en justifiant. -
On propose
def arret_essai1(programme): exec(programme) ; return True. Que réalise ce code, et permet-il de décider si un programme s’arrête ?Coup de pouce
Q4 : que se passe-t-il si le programme passé ne s’arrête pas ?
-
Une autre idée : décréter qu’un programme s’arrête s’il ne contient pas le mot
"while". Écrirearret_essai2(programme)qui renvoieTruesi"while"n’apparaît pas dansprogramme,Falsesinon. Montrer, par un contre-exemple de chaque type, que cette fonction se trompe.Coup de pouce
Q5 : il faut un cas où elle répond
Falseà tort et un cas où elle répondTrueà tort. Une bouclewhileest-elle le seul moyen de ne pas s’arrêter ? -
On suppose finalement disposer d’une vraie fonction
arret(programme)(renvoyantTruesiexec(programme)termine,Falsesinon). Écrireterminaison_inverse(programme)qui termine siprogrammene termine pas, et ne termine pas siprogrammetermine.Coup de pouce 2 (début de solution)
Q6 :
def terminaison_inverse(programme):
if arret(programme):
... -
On pose :
programme_paradoxal = "terminaison_inverse(programme_paradoxal)"
Étudier siexec(programme_paradoxal)termine, et conclure sur l’existence dearret. -
Cette impossibilité est-elle due à une limite du langage Python ?
Corrigé
Barème indicatif (sur 6) : Q1 : 0,5 ; Q2 : 0,5 ; Q3 : 1 ; Q4 : 0,75 ; Q5 : 0,75 ; Q6 : 0,75 ; Q7 : 1,25 ; Q8 : 0,5
-
À la fin :
xpasse de \(10\) à \(0\) (dix tours), etymonte de \(10\) à \(20\). Doncxvaut \(0\) etyvaut \(20\). -
Un programme Python est un texte (des caractères) : on peut donc le stocker dans une chaîne, la manipuler, et l’exécuter avec
exec. -
programme3:xvaut \(10,8,6,4,2,0\) \(\rightarrow\) termine.programme4:xcroît (\(10,12,14,\dots\)), le testx>0reste vrai \(\rightarrow\) ne termine pas.programme5:x=10et le testx<0est faux d’emblée, la boucle ne s’exécute pas \(\rightarrow\) termine aussitôt.programme6:xvaut \(10,6,2,-2,-6,\dots\) et ne vaut jamais \(0\) \(\rightarrow\) ne termine pas. -
arret_essai1exécute le programme puis renvoieTrue. Cela ne décide rien : si le programme passé ne s’arrête pas, alorsexec(programme)ne s’arrête pas non plus, et la fonction elle-même ne termine pas — donc ne renverra jamaisFalse. Or un décideur doit s’arrêter dans tous les cas. Ce code ne convient pas.
5.
```python
def arret_essai2(programme):
return "while" not in programme
```
**Contre-exemples.** (i) `True` alors que ça ne s’arrête pas : un programme **sans** `while` mais à **récursion infinie** (`def g(): g()` puis `g()`) ; il n’y a pas `while`, donc `arret_essai2` renvoie `True`, pourtant il ne s’arrête pas. (ii) `False` alors que ça s’arrête : `while False: pass` *contient* le mot `while` (donc `arret_essai2` renvoie `False`), pourtant le programme s’arrête immédiatement. La présence du mot « while » ne dit rien de la terminaison.
6.
```python
def terminaison_inverse(programme):
if arret(programme): # si le programme termine...
while True: # ... on boucle pour toujours
pass
else:
return # sinon on termine
```
-
Notons \(P\) la chaîne
programme_paradoxal, c’est-à-dire"terminaison_inverse(programme_paradoxal)". Exécuter \(P\) revient à évaluerterminaison_inverse(\(P\)). Siarret(\(P\))dit que \(P\) termine, alorsterminaison_inverseboucle \(\rightarrow\) \(P\) ne termine pas : contradiction. Siarret(\(P\))dit que \(P\) ne termine pas, alorsterminaison_inversetermine \(\rightarrow\) \(P\) termine : contradiction. Dans les deux casarretse trompe : une telle fonctionarretne peut pas exister (problème de l’arrêt). -
Non : l’impossibilité n’a rien à voir avec Python. Tout langage assez expressif (Turing-complet) permet d’écrire ce même paradoxe : le problème de l’arrêt est indécidable quel que soit le langage.
★★ Exercice 15 — Un correcteur automatique d’exercices (sujet maison, façon bac) #
≈ 45 min
Une plateforme d’entraînement en ligne corrige automatiquement les fonctions Python écrites par les élèves. Pour l’exercice « écrire une fonction qui renvoie le carré d’un entier n », elle dispose d’une liste de tests, chaque test étant un couple (entree, resultat_attendu), et de la fonction suivante :
def verifier(fonction, tests):
reussis = 0
for (entree, attendu) in tests:
if fonction(entree) == attendu:
reussis = reussis + 1
return reussis
tests = [(0, 0), (2, 4), (3, 9)]
Partie A — Un programme qui en examine un autre.
-
Quelle est la particularité du premier paramètre de la fonction
verifier? Quelle notion du cours cela illustre-t-il ? -
Quatre élèves ont rendu les fonctions suivantes :
def carre_a(n): def carre_c(n): return n * n resultat = 0 i = 0 def carre_b(n): while i != n: return 2 * n resultat = resultat + n i = i + 1 def carre_d(n): return resultat if n == 0: return 0 elif n == 2: return 4 else: return 9Donner la valeur renvoyée par
verifier(carre_a, tests),verifier(carre_b, tests),verifier(carre_c, tests)etverifier(carre_d, tests). -
Parmi les fonctions qui réussissent les trois tests, l’une est pourtant fausse pour la plupart des entiers positifs. Laquelle ? Donner une entrée qui le montre. Que peut-on en conclure sur ce que prouve une série de tests réussis ?
Coup de pouce
Q3 : essayer chaque fonction sur une entrée qui ne figure pas dans la liste de tests.
Partie B — Le piège de la terminaison.
-
Le professeur ajoute le test
(-3, 9)à la liste. Donner les valeurs successives deilors de l’appelcarre_c(-3). Cet appel termine-t-il ? Que se passe-t-il alors pour l’appelverifier(carre_c, tests)? -
Modifier la condition de la boucle
while(et, si besoin, l’instruction qui suit) pour quecarre_ctermine et renvoie le bon résultat pour tout entiern, y compris négatif. Justifier la terminaison à l’aide d’un variant.Coup de pouce
Q5 : la boucle doit faire autant de tours que la valeur absolue de
n. Un variant est un entier positif qui diminue strictement à chaque tour.On pourra utiliser la fonction
absde Python, qui renvoie la valeur absolue d’un nombre. -
Pour ne plus jamais rester bloquée, la plateforme interrompt toute fonction qui calcule pendant plus de \(2\) secondes et la déclare « non terminante ». Cette règle permet-elle de décider si une fonction termine ? Donner un cas où elle se trompe.
-
Le développeur de la plateforme rêve d’une fonction
arret(fonction, x)qui renverrait, toujours en un temps fini,Truesifonction(x)termine etFalsesinon. Pourquoi est-ce impossible ? Rappeler le nom de ce résultat et l’idée de sa démonstration (on ne demande pas de code). -
Il voudrait aussi une fonction
equivalentes(f, g)qui dirait, à coup sûr, si deux fonctions renvoient le même résultat pour toutes les entrées. Est-ce possible ? Citer le résultat du cours qui permet de répondre.
Corrigé
Barème indicatif (sur 6) : Q1 : 0,5 ; Q2 : 1 ; Q3 : 0,75 ; Q4 : 0,75 ; Q5 : 0,75 ; Q6 : 0,75 ; Q7 : 1 ; Q8 : 0,5
Partie A.
-
Le premier paramètre,
fonction, est lui-même une fonction, c’est-à-dire un programme :verifierest un programme qui prend un autre programme en argument (un programme est une donnée). -
verifier(carre_a, tests)renvoie3;verifier(carre_b, tests)renvoie2(\(2\times 0 = 0\) et \(2 \times 2 = 4\) sont justes, mais \(2\times 3 = 6 \ne 9\)) ;verifier(carre_c, tests)renvoie3(pour \(n \ge 0\), la boucle ajoute \(n\) exactement \(n\) fois) ;verifier(carre_d, tests)renvoie3. -
carre_dréussit les trois tests mais renvoie \(9\) pour toute autre entrée :carre_d(5)renvoie \(9\) au lieu de \(25\). Des tests réussis ne prouvent pas qu’une fonction est correcte : ils ne vérifient qu’un nombre fini d’entrées, alors qu’il y en a une infinité.
Partie B.
-
ivaut \(0, 1, 2, 3, \dots\) : il augmente et ne vaudra jamais \(-3\). L’appelcarre_c(-3)ne termine pas, doncverifier(carre_c, tests)ne termine pas non plus : la plateforme reste bloquée. -
Par exemple :
def carre_c(n): resultat = 0 i = 0 while i < abs(n): resultat = resultat + abs(n) i = i + 1 return resultatOn ajoute \(|n|\) exactement \(|n|\) fois, donc on obtient \(|n| \times |n| = n^2\). Variant : \(|n| - i\) est un entier positif tant qu’on est dans la boucle et il diminue de \(1\) à chaque tour ; il ne peut pas décroître indéfiniment, donc la boucle s’arrête (après \(|n|\) tours).
-
Non. Ce n’est pas une décision de l’arrêt : une fonction correcte mais lente (par exemple
carre_c(10**9), qui fait un milliard de tours de boucle) dépasse les \(2\) secondes et serait déclarée « non terminante » alors qu’elle termine. La règle se trompe dans ce sens-là ; seule la réponse « termine » est sûre. -
C’est le problème de l’arrêt, indécidable (Turing, 1936). Idée : si
arretexistait, on écrirait un programmeparadoxequi, appliqué à lui-même, boucle siarretprédit qu’il s’arrête et s’arrête siarretprédit qu’il boucle ; dans les deux casarretse trompe : contradiction, doncarretn’existe pas. -
Non. Savoir si deux fonctions renvoient toujours le même résultat est une question non triviale sur leur comportement : elle est indécidable d’après le théorème de Rice. (Et les tests n’y suffisent pas, cf. question 3.)
★★★ Exercice 16 — Un antivirus infaillible ? (sujet maison, façon bac) #
≈ 45 min
Une jeune entreprise affirme avoir mis au point un antivirus « infaillible ». Son cœur est une fonction dangereux(code) qui prend en paramètre le code source d’un programme Python (une chaîne de caractères) et qui renverrait True si l’exécution de ce programme appelle un jour la fonction formater_disque(), et False sinon.
Partie A — Une première version, purement textuelle.
-
La question « le programme
codeappelle-t-il un jourformater_disque()? » est-elle un problème de décision ? Préciser la donnée d’entrée et les réponses possibles. -
Un stagiaire propose de chercher simplement le texte
"formater_disque"dans le code. Écrire une fonctioncontient(texte, motif)qui renvoieTruesi la chaînemotifapparaît dans la chaînetexte, etFalsesinon, sans utiliser l’opérateurinsur les chaînes.Coup de pouce
Q2 : comparer
motifà chaque tranchetexte[i:i + len(motif)]: quelles valeurs deifaut-il essayer ?Coup de pouce 2 (début de solution)
Q2 :
def contient(texte, motif):
n = len(motif)
for i in range(len(texte) - n + 1): -
On pose :
def analyse_textuelle(code): return contient(code, "formater_disque")Cette fonction s’arrête-t-elle toujours ? Quel problème décide-t-elle exactement ?
-
On considère les trois programmes suivants :
code1 = "if 1 > 2:\n formater_disque()\n" code2 = "# surtout ne jamais appeler formater_disque\nx = 0\n" code3 = "nom = 'formater_' + 'disque'\nexec(nom + '()')\n"Pour chacun, donner la réponse de
analyse_textuelle, puis dire si son exécution formate réellement le disque. Lesquels sont des « fausses alertes » ? Lequel est un virus « non détecté » ?
Partie B — L’antivirus parfait n’existe pas.
On suppose maintenant qu’une fonction dangereux(code) parfaite existe : elle s’arrête toujours et ne se trompe jamais. Soit programme le code source d’un programme quelconque qui, lui, n’appelle jamais formater_disque().
-
On fabrique la chaîne
piege = programme + "\nformater_disque()\n", c’est-à-dire le même programme suivi d’un appel àformater_disque(). On suppose queprogrammene provoque jamais d’erreur. Expliquer pourquoi l’exécution depiegeappelleformater_disque()si et seulement si l’exécution deprogrammetermine. -
En déduire une fonction
arret(programme), de deux lignes, qui utilisedangereuxet renvoieTruesi l’exécution deprogrammetermine,Falsesinon.Coup de pouce
Q6 : d’après la question 5, demander si
piegeest dangereux revient à poser une autre question surprogramme: laquelle ? -
Quelle contradiction obtient-on ? Que peut-on en conclure sur la fonction
dangereux? -
Quel théorème du cours généralise ce raisonnement ? Les antivirus réels sont-ils pour autant inutiles ? Que doit-on accepter en les utilisant ?
Corrigé
Barème indicatif (sur 6) : Q1 : 0,5 ; Q2 : 1 ; Q3 : 0,5 ; Q4 : 1 ; Q5 : 0,75 ; Q6 : 0,75 ; Q7 : 0,75 ; Q8 : 0,75
Partie A.
-
Oui : la donnée d’entrée est le code source d’un programme (une chaîne de caractères), et la réponse est oui (
True) ou non (False). -
Par exemple :
def contient(texte, motif): n = len(motif) for i in range(len(texte) - n + 1): if texte[i:i + n] == motif: return True return False -
Oui, elle s’arrête toujours : la boucle
forparcourt un nombre fini de positions. Mais elle décide le problème « le texte du programme contient-il le motformater_disque? », qui porte sur le texte du programme et non sur son comportement. -
code1:True, mais1 > 2est faux, l’appel n’a jamais lieu \(\rightarrow\) fausse alerte.code2:True, mais le mot n’apparaît que dans un commentaire \(\rightarrow\) fausse alerte.code3:False(le mot complet n’apparaît pas, il est fabriqué par concaténation), pourtantexecexécute bienformater_disque()\(\rightarrow\) virus non détecté.
Partie B.
-
piegeexécute d’abordprogramme, qui n’appelle jamaisformater_disque(), puis l’appel ajouté. Siprogrammetermine, on arrive à la dernière ligne etformater_disque()est appelée. S’il ne termine pas, on n’atteint jamais cette ligne et la fonction n’est jamais appelée. Doncpiegeest dangereux si et seulement siprogrammetermine. -
On peut écrire :
def arret(programme): return dangereux(programme + "\nformater_disque()\n")Elle s’arrête toujours (car
dangereuxs’arrête toujours) et donne la bonne réponse d’après la question précédente. -
On aurait écrit une fonction qui décide le problème de l’arrêt, ce qui est impossible (Turing). L’hypothèse de départ est donc fausse : la fonction
dangereuxparfaite ne peut pas exister. -
Le théorème de Rice : toute question non triviale sur le comportement d’un programme est indécidable. Les antivirus réels restent très utiles (signatures de virus connus, analyse des comportements suspects, exécution surveillée…), mais ils doivent accepter de se tromper parfois : des fausses alertes et des virus non détectés. Aucun ne peut être infaillible.
★★★ Exercice 17 — Un interpréteur miniature (sujet maison, façon bac) #
≈ 60 min
On invente un langage de programmation minuscule. Un programme agit sur une seule variable x, un entier positif ou nul ; c’est une liste d’instructions, numérotées à partir de \(0\), parmi :
-
"inc": augmenterxde \(1\) et passer à l’instruction suivante ; -
"dec": diminuerxde \(1\) (sauf s’il vaut déjà \(0\)) et passer à l’instruction suivante ; -
"sinul k": sixvaut \(0\), aller à l’instruction numérok, sinon passer à la suivante ; -
"aller k": aller à l’instruction numérok.
Le programme s’arrête lorsque le numéro de l’instruction à exécuter dépasse la dernière instruction. On donne trois programmes :
P1 = ["sinul 3", "dec", "aller 0"]
P2 = ["inc", "aller 0"]
P3 = ["sinul 6", "dec", "sinul 5", "dec", "aller 0", "aller 5"]
Un interpréteur Python de ce langage exécute au plus limite instructions ; il renvoie la valeur finale de x si le programme s’est arrêté, et None si la limite a été atteinte avant :
def executer(programme, x, limite):
i = 0 # numero de l'instruction courante
etapes = 0 # nombre d'instructions deja executees
while i < len(programme):
if etapes == limite:
return None
mots = programme[i].split() # "sinul 3" -> ["sinul", "3"]
if mots[0] == "inc":
x = x + 1
i = i + 1
elif mots[0] == "dec":
...
elif mots[0] == "sinul":
...
elif mots[0] == "aller":
i = int(mots[1])
etapes = etapes + 1
return x
-
La fonction
executerreçoit un programme en paramètre. Sous quelle forme ? Citer un programme d’usage courant qui fonctionne sur le même principe. -
Recopier et compléter les deux blocs
...(instructions"dec"et"sinul").Coup de pouce
Q2 : pour
"dec", penser au cas oùxvaut déjà \(0\) ; pour"sinul",mots[1]est une chaîne, et il y a deux cas selon la valeur dex.Coup de pouce 2 (début de solution)
Q2 :
elif mots[0] == "dec":
if x > 0:
x = x - 1
i = i + 1 -
Dérouler l’exécution de
P1avecxvalant \(2\) au départ : donner, à chaque étape, le numéro de l’instruction exécutée et la valeur dex. Que renvoieexecuter(P1, 2, 100)? Combien d’instructions ont été exécutées ? Plus généralement, que faitP1? -
Que renvoie
executer(P2, 0, 1000)? Le programmeP2s’arrête-t-il ? Justifier. -
Pour quelles valeurs de départ de
xle programmeP3s’arrête-t-il ? Justifier en décrivant ce que fait chaque passage dans la boucle des instructions \(0\) à \(4\), et le rôle de l’instruction numéro \(5\).Coup de pouce
Q5 : dérouler
P3avecxvalant \(2\), puis \(3\), et comparer les deux exécutions. -
On propose la fonction suivante :
def arret_limite(programme, x): return executer(programme, x, 10**6) is not NoneSachant que
P1exécute \(3x + 1\) instructions lorsqu’il démarre avec la valeurx, que renvoiearret_limite(P1, 10**6)? Est-ce la bonne réponse ? Augmenter la limite résoudrait-il le problème en général ? -
En revanche, lorsque
arret_limiterenvoieTrue, peut-on être certain que le programme s’arrête ? Expliquer la dissymétrie entre les deux réponses. -
On enrichit ce langage (plusieurs variables, nouvelles instructions) jusqu’à le rendre Turing-complet. Existe-t-il alors un programme qui décide, pour tout programme de ce langage et toute valeur de départ, s’il s’arrête ? Justifier à l’aide du cours.
Corrigé
Barème indicatif (sur 8) : Q1 : 0,5 ; Q2 : 1 ; Q3 : 1 ; Q4 : 1 ; Q5 : 1,25 ; Q6 : 1 ; Q7 : 1 ; Q8 : 1,25
-
Le programme est donné sous forme d’une liste de chaînes de caractères : c’est une simple donnée.
executerest un interpréteur, exactement commepython3, qui lit et exécute le texte d’un programme Python. -
On complète ainsi :
elif mots[0] == "dec": if x > 0: x = x - 1 i = i + 1 elif mots[0] == "sinul": if x == 0: i = int(mots[1]) else: i = i + 1 -
Déroulé (numéro de l’instruction exécutée, valeur de
xavant l’instruction) : \((0, 2)\), \((1, 2)\), \((2, 1)\), \((0, 1)\), \((1, 1)\), \((2, 0)\), \((0, 0)\) ; à cette dernière étape,xvaut \(0\), on saute à l’instruction \(3\), qui n’existe pas : le programme s’arrête.executer(P1, 2, 100)renvoie0, après \(7\) instructions.P1remetxà zéro (en le diminuant de \(1\) à chaque tour). -
P2augmentexpuis retourne à l’instruction \(0\), indéfiniment : il n’y a aucune instruction de saut vers la fin, le numéro d’instruction vaut toujours \(0\) ou \(1\). Il ne s’arrête jamais, etexecuter(P2, 0, 1000)renvoieNone(limite atteinte). -
Un passage dans les instructions \(0\) à \(4\) : si
xvaut \(0\), on sort (saut en \(6\)) ; sinon on retire \(1\) ; sixvaut alors \(0\), on saute en \(5\) ; sinon on retire encore \(1\) et on recommence. Chaque tour retire donc \(2\). Sixest pair, on finit par trouverx\(= 0\) à l’instruction \(0\) : le programme s’arrête. Sixest impair, on trouvex\(= 0\) à l’instruction \(2\) et l’on saute en \(5\), où"aller 5"renvoie sans fin sur elle-même : le programme ne s’arrête pas.P3s’arrête si et seulement sixest pair. -
P1avec \(x = 10^6\) exécute \(3 \times 10^6 + 1\) instructions, plus que la limite de \(10^6\) :executerrenvoieNone, doncarret_limite(P1, 10**6)renvoieFalse. C’est faux :P1s’arrête toujours. Augmenter la limite ne règle rien en général : quelle que soit la limite \(L\), il suffit de prendre \(x\) tel que \(3x + 1 > L\) pour la mettre en défaut. -
Oui : si
executerrenvoie une valeur, c’est que le programme s’est effectivement arrêté (on l’a vu s’arrêter). En revanche,Nonesignifie seulement « pas encore arrêté » : on ne peut pas savoir s’il s’arrêterait un peu plus tard ou jamais. Exécuter permet de constater l’arrêt, jamais de prouver qu’il n’aura pas lieu. -
Non. Pour un langage Turing-complet, le problème de l’arrêt est indécidable : le raisonnement de Turing (programme
paradoxequi se prend lui-même en donnée) s’y transpose, car l’indécidabilité ne dépend pas du langage.
Critiquer une réponse d’IA#
★★ Exercice 18 — Le problème de l’arrêt expliqué par un assistant d’IA #
Un élève demande à un assistant d’IA : « Le problème de l’arrêt est indécidable : cela veut-il dire qu’on ne peut jamais savoir si un programme s’arrête ? » Voici la réponse obtenue :
Réponse de l’assistant
« Le problème de l’arrêt consiste à décider, pour un programme P et une entrée E quelconques, si P lancé sur E s’arrête. Alan Turing a démontré en 1936 qu’aucun algorithme ne peut le résoudre dans tous les cas : on suppose qu’un tel programme halt existe, on construit un programme paradoxe qui fait le contraire de ce que halt prédit sur lui-même, et l’on aboutit à une contradiction. Ce résultat ne dépend pas du langage : il vaut pour tout langage Turing-complet, Python compris. Conséquence directe : il est impossible de prouver qu’un programme donné s’arrête ; la seule chose que l’on puisse faire est de l’exécuter et d’attendre. C’est aussi pourquoi de nombreuses questions sur le comportement d’un programme (renvoie-t-il toujours 0 ? affiche-t-il un jour quelque chose ?) sont elles aussi indécidables. »
-
La réponse est-elle correcte ? Pour le vérifier, considérer le programme ci-dessous : peut-on affirmer, sans l’exécuter, qu’il s’arrête ? Comment ?
n = 10 while n > 0: n = n - 1 -
Localiser et corriger la phrase erronée.
Coup de pouce
La question 1 montre qu’on peut prouver l’arrêt d’un programme sans l’exécuter : quelle phrase de la réponse affirme le contraire ?
-
Quelle vérification simple, faite avant de faire confiance à la réponse, aurait suffi à détecter l’erreur ?
Corrigé
-
Non. Le programme proposé s’arrête, et on le prouve sans l’exécuter :
nest un entier positif qui décroît strictement à chaque tour (un variant de boucle, vu en Première), donc la boucle fait exactement \(10\) tours. On sait donc parfaitement, au cas par cas, prouver qu’un programme s’arrête. -
La phrase fautive est « il est impossible de prouver qu’un programme donné s’arrête ; la seule chose que l’on puisse faire est de l’exécuter et d’attendre ». Elle contredit le cours : « on peut très bien, au cas par cas, prouver qu’un programme donné s’arrête (avec un variant de boucle). Ce qui est impossible, c’est une unique méthode automatique et universelle qui marcherait sur tous les programmes ». Correction : il n’existe aucun algorithme général qui décide l’arrêt de tous les programmes ; pour un programme particulier, une preuve (variant, raisonnement) reste possible. Tout le reste de la réponse (énoncé, date, schéma de la preuve, indépendance du langage, contagion aux autres questions de comportement) est exact.
-
Confronter chaque affirmation générale à un exemple concret que l’on maîtrise : le premier programme à boucle bornée venu réfute « impossible de prouver ». Plus généralement, relire la définition (« pour toute entrée », « un algorithme ») : indécidable signifie « pas de méthode universelle », pas « jamais de réponse ».
S’entraîner à l’oral#
★★ Exercice 19 — Expliquer en deux minutes #
Au Grand Oral comme devant l’examinateur de l’épreuve pratique, il faut savoir expliquer une notion clairement, sans lire ses notes. Choisir l’un des sujets suivants (ou le tirer au sort) :
-
Expliquer à un camarade de Première en quoi « un programme est aussi une donnée », avec des exemples.
-
Expliquer, sans formalisme, l’idée de la preuve que le problème de l’arrêt est indécidable.
-
Expliquer pourquoi « indécidable » ne veut pas dire « on ne sait jamais répondre ».
Préparation (5 min, seul). Noter au brouillon trois idées et un exemple, rien de plus : comme au Grand Oral, ces notes servent d’aide-mémoire. Exposé (2 min, chronométré). Debout, face à un camarade, sans lire ses notes et sans rien écrire : tout passe par la parole. Retour (2 min). Le camarade pose une question de relance (on peut alors s’aider d’un schéma), remplit la grille ci-dessous et donne un conseil ; puis on échange les rôles. Cette grille reprend les critères d’évaluation du Grand Oral.
Coup de pouce
Structurer chaque explication en trois temps : une définition en une phrase, un exemple petit et concret déroulé à voix haute, puis le piège (l’erreur que l’auditeur risque de faire). Finir par une phrase qui répond à la question posée. Sujet 2 : c’est un raisonnement par l’absurde ; annoncer ce que l’on suppose, construire le programme « contradictoire », puis poser la question qui fâche. S’entraîner à le dire en moins d’une minute.
| Grille entre camarades | Oui | En partie | Pas encore |
|---|---|---|---|
| Clair — audible, posé ; chaque mot technique est expliqué | ▫ | ▫ | ▫ |
| Juste — c’est exact, et l’exemple montre vraiment l’idée | ▫ | ▫ | ▫ |
| Construit — un fil conducteur, tenu en deux minutes, sans lire | ▫ | ▫ | ▫ |
| Un conseil pour la prochaine fois : | |||
Corrigé
Pas de texte à apprendre par cœur : voici les éléments attendus pour chaque sujet. L’ordre, les mots et l’exemple peuvent être différents ; l’explication est réussie si ces idées y sont, justes et reliées entre elles.
Sujet 1.
-
Un programme est un texte (une suite de caractères, donc de bits) : un autre programme peut le lire, le transformer ou l’exécuter.
-
Exemples : l’interpréteur Python (il reçoit un programme et l’exécute), un compilateur, un antivirus, un correcteur automatique d’exercices.
-
Exemple simple : un programme Python qui compte les lignes d’un fichier
.py. -
Conséquence : on peut se demander si un programme pourrait analyser tous les programmes ; c’est l’origine du problème de l’arrêt.
Sujet 2.
-
Problème : écrire
arret(prog, entree)qui renvoieTruesiprogs’arrête surentreeetFalsesinon, pour tous les programmes. -
Par l’absurde : on suppose qu’elle existe et on construit
paradoxe(prog), qui boucle sans fin siarret(prog, prog)vautTrueet s’arrête sinon. -
Question :
paradoxe(paradoxe)s’arrête-t-il ? S’il s’arrête,arretrépondTrue, donc il boucle ; s’il boucle,arretrépondFalse, donc il s’arrête : contradiction. -
Conclusion (Turing, 1936) : aucun programme ne peut décider l’arrêt de tous les programmes.
Sujet 3.
-
Un problème de décision est décidable s’il existe un algorithme qui répond correctement (oui ou non), en un temps fini, pour toute entrée.
-
Indécidable : aucun algorithme unique ne fonctionne pour toutes les entrées.
-
Exemple : on prouve facilement qu’un programme précis, avec une seule boucle
for, s’arrête ; c’est la méthode universelle qui n’existe pas. -
Piège : confondre avec « trop long à calculer » (question de coût, P et NP) ; indécidable signifie impossible même avec un temps illimité.