Problème de l'arrêt d'un programme

La notion d'algorithme stipule qu'il doit, à partir d'une entrée, rendre un calcul en un temps fini.

La définition d'un programme stipule qu'il doit respecter 3 propriétés faciles à vérifier, il suffit que :

Mais qu'en est-il de la condition de terminaison (le programme s'arrête après un temps fini quelle que soit son entrée) pour définir un algorithme ? Cette question est cruciale et... compliquée.

On se place ici, sans perte de généralité, dans le cadre des programmes prenant un entier en paramètre et rendant un entier (par défaut 0).

Problème de l'arrêt

Savoir si un programme va s'arrêter peut être très simple. Le programme suivant s'arrête tout le temps :

Nom : compte à rebours
Entrée :
    n : un entier strictement positif
Programme :
    tant que n est strictement plus grand que 0:
        dire à voix haute : "Explosion dans"
        dire à voix haute la valeur de n
        dire à voix haute : "secondes"
        attendre 1 seconde
        décrémenter la valeur de n de 1
    dire à voix haute : "BOUM."
    rendre 0

Alors que celui-ci ne s'arrête jamais :

Nom : vérité
Entrée :
    n : un entier strictement positif
Programme :
    tant que n est strictement plus grand que 0:
        dire à voix haute : "L'informatique c'est magnifique !"

Mais si on prend le programme suivant qui implémente la célèbre conjecture de Syracuse, on ne sait pas vraiment (on n'a pas de démonstration) s'il s'arrête tout le temps :

Nom : syracuse
Entrée :
    n : un entier strictement positif
Programme :
    tant que n est strictement plus grand que 1:
        si n est pair:
            diviser n par 2
        sinon:
            multiplier n par 3 et lui ajouter 1
    rendre 1

De nombreux travaux ont été faits pour mieux comprendre ce problème. Vous pouvez aller jeter un coup d'œil aux liens suivants pour (beaucoup) plus d'informations :

Testez le programme suivant écrit en python pour quelques entrées. Vous verrez que très rapidement $n$ va être égal à 1.

def syracuse(x):
    while x != 1:
        print(x)
        if x % 2 == 0:
            x = x // 2
        else:
            x = 3 * x + 1
    return 1

x = int(input("Tapez un entier (puis entrée) : "))
syracuse(x)

Le problème de l'arrêt d'un programme n'est donc pas simple. On ne peut pas le vérifier expérimentalement puisqu'attendre un temps infini avant de conclure n'est pas possible.

Si vous voulez voir en vrai un programme qui ne s'arrête pas, regardez cette calculatrice ancienne tentant de diviser par zéro

Il n'y a pas de méthode générale pour le faire et c'est ce que nous allons démontrer. Supposons que l'on puisse automatiser le processus de vérification. Il existerait alors un algorithme permettant de le faire, nommons-le stop. Il prendrait en paramètre un entier représentant un programme (on sait qu'on peut le faire) et rendrait 1 si le programme en paramètre est un algorithme et 0 sinon :

Nom : stop
Entrée :
    n : un entier représentant un programme
Sortie :
    rend 1 si le programme en entrée est un algorithme
    rend 0 sinon

Notez que la solution consistant à exécuter le programme en entrée pour voir s'il s'arrête n'est pas une solution valide car si ce dernier ne s'arrête pas stop ne s'arrêtera pas non plus, ce qui est impossible puisque c'est un algorithme. C'est Alan Turing lui-même qui a montré qu'un tel algorithme ne peut exister dans le cadre de ses machines (c'est le problème de l'arrêt de la machine de Turing) :

Théorème (Turing, 1936)

L'algorithme stop n'existe pas.

preuve

Supposons que stop soit un algorithme et construisons le programme suivant :

Nom : oups
Programme :
    si le retour de l'algorithme stop avec comme entrée le numéro de oups vaut 1 (si stop(oups) == 1):
        répéter:             # c'est une boucle infinie
            ne rien faire 
    sinon:
        rendre 0

Le programme oups ne peut exister puisque :

  • oups est un programme, il a donc un numéro $N$,
  • si oups est un algorithme alors stop(N) va rendre 1 ce qui fait que oups ne s'arrêtera pas,
  • si oups n'est pas un algorithme alors stop(N) va rendre 0 ce qui fait que oups s'arrêtera.

Si oups ne peut pas exister, stop ne le peut pas non plus, ce qui contredit notre hypothèse.

Comprenez bien le théorème ci-dessus. Il signifie qu'il n'existe pas de propriété démontrable (donc en temps fini et donc pouvant être écrite comme un algorithme puisqu'une preuve mathématique est un algorithme et réciproquement) qu'auraient tous les algorithmes et qui les différencierait des programmes. Si on peut montrer qu'un programme s'arrête, il faut faire la preuve pour ce programme spécifiquement.

À retenir

Lorsque vous créez des algorithmes, il faut toujours démontrer qu'ils s'arrêtent.

Que calcule un algorithme

Cette partie est consacrée au Théorème de Rice

S'il est impossible de savoir a priori si un programme s'arrête ou pas, il est aussi impossible de savoir ce qu'il va faire... Il n'existe en effet pas de démonstration qu'un algorithme possède une propriété donnée, et ce, quelle que soit celle-ci.

Commençons par définir ce qu'est une propriété :

Définition

Une propriété $P$ est un sous-ensemble non trivial d'algorithmes, c'est-à-dire que :

  • il existe au moins un algorithme dans $P$
  • il existe au moins un algorithme qui n'est pas dans $P$

Un algorithme $A$ satisfait la propriété $P$ s'il existe $A'\in P$ tel que $A(e) = A'(e)$ pour toute entrée $e$.

La notion de propriété permet de rendre compte du sens des algorithmes, de ce qu'ils font : les programmes faisant la même chose font partie de la même propriété. Par exemple :

On va là aussi donner un résultat négatif : il n'existe pas d'algorithme permettant de savoir si un programme respecte une propriété donnée. Il faut faire la démonstration pour chaque programme et chaque propriété.

Théorème (Rice, 1953)

Quelle que soit la propriété $P$, l'algorithme suivant n'existe pas :

Nom : propriété-P
Entrée :
    n : un entier représentant un programme
Sortie :
    1 si le programme en entrée vérifie la propriété P
    0 sinon

preuve

Soit $A_0 \in P$ et $A$ un programme. On peut alors construire le programme suivant :

Nom : stop-via-P
Entrée :
    n : un entier
Programme :
    A(n)
    rendre A_0(n)

Le programme stop-via-P vérifie la propriété $P$ si et seulement si $A$ est un algorithme (s'il s'arrête). On en conclut que l'algorithme propriété-P appliqué au numéro de stop-via-P va rendre 1 si le programme A s'arrête et 0 sinon : c'est une autre façon d'écrire le programme stop !

Comme l'algorithme stop ne peut pas exister, propriété-P non plus.

Ceci signifie que pour toute propriété voulue sur la sortie d'un algorithme il faut la vérifier pour toutes les entrées. Si on veut démontrer qu'un algorithme a une propriété donnée, il faut le démontrer pour cet algorithme, et il n'existe pas de preuve générale.

Réciproquement, quelle que soit la tâche à effectuer on ne peut pas connaître les algorithmes qui l'effectueront. Il est par exemple impossible de savoir si un programme calcule $n!$, en revanche il est parfois possible de démontrer qu'un programme donné le fait.

Il est donc nécessaire :

À retenir

Lorsque vous créez des algorithmes, il faut toujours :

  1. expliciter ce que vous pensez qu'il fait,
  2. démontrer qu'il le fait.

Il n'y a pas de condition nécessaire et suffisante qui garantirait qu'un algorithme fait ce que l'on pense qu'il fait et ce, quoi que l'on pense qu'il fasse.