Aller au contenu

Chapitre 4 - Partie 3 : Calculabilité et décidabilité


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.

De nombreux logiciels prennent des programmes en entrée :

LogicielCe qu'il fait avec le programme
Interpréteur PythonLit le code source et l'exécute ligne par ligne
Compilateur (C, Java)Traduit le code source en langage machine
Éditeur de codeAffiche le code avec coloration syntaxique
DébogueurExécute le code pas à pas, inspecte les variables
Système d'exploitationCharge le programme en mémoire et crée un processus
AntivirusAnalyse le programme pour détecter des comportements malveillants

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.")

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.

FonctionCalculable ?
Additionner deux entiersOui
Trier un tableauOui
Vérifier si un nombre est premierOui
Le problème de l'arrêtNon (voir plus loin)

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.


Un problème de décision est une question qui admet une réponse oui ou non pour chaque instance.

ProblèmeQuestionDé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
  • 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.

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 ?

# 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é !

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) renvoie True (paradoxe s'arrête sur paradoxe). → Alors paradoxe(paradoxe) entre dans la boucle infinie → il ne s'arrête pas. Contradiction.

  • Cas 2 : arret(paradoxe, paradoxe) renvoie False (paradoxe ne s'arrête pas sur paradoxe). → Alors paradoxe(paradoxe) exécute return → 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.

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).

DateÉvénement
1936Alan Turing prouve l'indécidabilité du problème de l'arrêt et invente la machine de Turing
1936Alonzo Church développe le lambda-calcul (équivalent à la machine de Turing)
1936Thèse de Church-Turing : tout calcul effectif peut être réalisé par une machine de Turing
1943Kurt Gödel avait déjà montré (1931) les limites des systèmes formels (théorèmes d'incomplétude)

ConceptDescription
Programme = donnéeUn programme est un fichier manipulable par d'autres programmes
CalculabilitéCaractérise ce qui peut être calculé par un algorithme
Thèse de Church-TuringTous 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êtImpossible 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