Partie 2 : Calculabilité et décidabilité¶
Programme officiel (B.O.)¶
B.O. spécial n° 8 du 25 juillet 2019 - NSI Terminale
| Contenus | Capacités attendues | Commentaires |
|---|---|---|
| Notion de programme en tant que donnée. Calculabilité, décidabilité. | Comprendre que tout programme est aussi une donnée. Comprendre que la calculabilité ne dépend pas du langage de programmation utilisé. Montrer, sans formalisme théorique, que le problème de l'arrêt est indécidable. | L'utilisation d'un interpréteur ou d'un compilateur, le téléchargement de logiciel, le fonctionnement des systèmes d'exploitation permettent de comprendre un programme comme donnée d'un autre programme. |
1. Un programme est aussi une donnée¶
1.1 Le programme comme fichier¶
Un programme (code source) est un fichier texte stocké sur le disque, exactement comme un document ou une image. Il peut être :
- lu par un éditeur de texte ;
- copié, déplacé, supprimé comme tout fichier ;
- envoyé par e-mail ou téléchargé sur Internet ;
- analysé par un autre programme.
1.2 Un programme traité par un autre programme¶
De nombreux logiciels prennent des programmes en entrée :
| Logiciel | Ce qu'il fait avec le programme |
|---|---|
| Interpréteur Python | Lit le code source et l'exécute ligne par ligne |
| Compilateur (C, Java) | Traduit le code source en langage machine |
| Éditeur de code | Affiche le code avec coloration syntaxique |
| Débogueur | Exécute le code pas à pas, inspecte les variables |
| Système d'exploitation | Charge le programme en mémoire et crée un processus |
| Antivirus | Analyse le programme pour détecter des comportements malveillants |
1.3 Conséquence fondamentale¶
Puisqu'un programme est une donnée, un programme peut se prendre lui-même en entrée. Cette observation, apparemment anodine, conduit à des résultats théoriques profonds sur les limites de l'informatique.
# Un programme qui lit son propre code source
with open(__file__, 'r') as f:
mon_code = f.read()
print(f"Ce programme fait {len(mon_code)} caractères.")
2. Calculabilité¶
2.1 Qu'est-ce qu'une fonction calculable ?¶
Une fonction est calculable s'il existe un algorithme (un programme) qui, pour chaque entrée, produit le résultat correct en un nombre fini d'étapes.
| Fonction | Calculable ? |
|---|---|
| Additionner deux entiers | Oui |
| Trier un tableau | Oui |
| Vérifier si un nombre est premier | Oui |
| Le problème de l'arrêt | Non (voir plus loin) |
2.2 La thèse de Church-Turing¶
Repère historique
En 1936, Alan Turing (machine de Turing) et Alonzo Church (lambda-calcul) ont montré, indépendamment, que leurs modèles de calcul étaient équivalents. La thèse de Church-Turing affirme que tout ce qui est intuitivement calculable peut être calculé par une machine de Turing.
2.3 L'universalité des langages de programmation¶
Tous les langages de programmation « complets » (Python, C, Java, JavaScript, etc.) sont équivalents en puissance de calcul : tout ce qui peut être calculé dans un langage peut l'être dans un autre.
Cela signifie que la calculabilité ne dépend pas du langage choisi. Si un problème est incalculable, aucun langage ne pourra le résoudre.
En pratique
Les langages diffèrent en termes de facilité d'utilisation, de performances et de domaines d'application, mais pas en termes de ce qu'ils peuvent calculer en théorie.
3. La décidabilité¶
3.1 Problème de décision¶
Un problème de décision est une question qui admet une réponse oui ou non pour chaque instance.
| Problème | Question | Décidable ? |
|---|---|---|
| Nombre premier | « n est-il premier ? » | Oui |
| Tri | « Le tableau est-il trié ? » | Oui |
| Arrêt | « Le programme P s'arrête-t-il sur l'entrée E ? » | Non |
3.2 Problème décidable vs indécidable¶
- Un problème est décidable s'il existe un algorithme qui donne toujours la bonne réponse (oui ou non) en un temps fini.
- Un problème est indécidable s'il n'existe aucun algorithme qui puisse répondre correctement dans tous les cas.
4. Le problème de l'arrêt¶
4.1 Énoncé¶
Le problème de l'arrêt est la question suivante :
Étant donné un programme P et une entrée E, peut-on déterminer si P s'arrête (termine) lorsqu'on l'exécute sur E, ou bien s'il boucle indéfiniment ?
4.2 Exemples¶
# Ce programme s'arrête-t-il ?
def programme_a(n):
return n + 1
# → Oui, il s'arrête toujours.
# Et celui-ci ?
def programme_b(n):
while True:
pass
# → Non, il boucle indéfiniment.
# Et celui-ci ?
def programme_c(n):
while n != 1:
if n % 2 == 0:
n = n // 2
else:
n = 3 * n + 1
return n
# → Conjecture de Syracuse : on pense que oui pour tout n,
# mais personne ne l'a prouvé !
4.3 Preuve d'indécidabilité (par l'absurde)¶
Supposons qu'il existe une fonction arret(P, E) qui renvoie True si le programme P s'arrête sur l'entrée E, et False sinon.
Construisons alors le programme paradoxal suivant :
def paradoxe(P):
if arret(P, P): # si P s'arrête quand on lui donne P
while True: # alors boucler indéfiniment
pass
else: # si P ne s'arrête pas
return # alors s'arrêter
Que se passe-t-il quand on appelle paradoxe(paradoxe) ?
-
Cas 1 :
arret(paradoxe, paradoxe)renvoieTrue(paradoxe s'arrête sur paradoxe). → Alorsparadoxe(paradoxe)entre dans la boucle infinie → il ne s'arrête pas. Contradiction. -
Cas 2 :
arret(paradoxe, paradoxe)renvoieFalse(paradoxe ne s'arrête pas sur paradoxe). → Alorsparadoxe(paradoxe)exécutereturn→ il s'arrête. Contradiction.
Dans les deux cas, on obtient une contradiction. Donc notre hypothèse est fausse : la fonction arret ne peut pas exister.
4.4 Conséquences pratiques¶
Le problème de l'arrêt étant indécidable, il est impossible de créer un programme qui détecte automatiquement, dans tous les cas :
- si un programme contient une boucle infinie ;
- si un programme terminera sur une entrée donnée ;
- si deux programmes font la même chose (équivalence).
En pratique
Les outils d'analyse de code (linters, débogueurs) peuvent détecter certains cas de boucle infinie ou de bug, mais jamais tous les cas. C'est une limitation fondamentale de l'informatique, pas un manque de technologie.
5. Repères historiques¶
| Date | Événement |
|---|---|
| 1936 | Alan Turing prouve l'indécidabilité du problème de l'arrêt et invente la machine de Turing |
| 1936 | Alonzo Church développe le lambda-calcul (équivalent à la machine de Turing) |
| 1936 | Thèse de Church-Turing : tout calcul effectif peut être réalisé par une machine de Turing |
| 1943 | Kurt Gödel avait déjà montré (1931) les limites des systèmes formels (théorèmes d'incomplétude) |
À retenir¶
| Concept | Description |
|---|---|
| Programme = donnée | Un programme est un fichier manipulable par d'autres programmes |
| Calculabilité | Caractérise ce qui peut être calculé par un algorithme |
| Thèse de Church-Turing | Tous les langages de programmation sont équivalents en puissance de calcul |
| Décidabilité | Un problème est décidable s'il existe un algorithme qui répond toujours oui/non |
| Problème de l'arrêt | Impossible de créer un programme qui détermine si tout programme termine |
| Indécidabilité | Certains problèmes ne peuvent être résolus par aucun algorithme |