Au delà de tout problème de complexité, de rapidité d'exécution ou de puissance des machines, il existe des limites théoriques à ce que peut faire un ordinateur.
Nous allons aborder dans cette partie un aspect très théorique de l'informatique à travers les limites des problèmes que peut résoudre cette science.
Nous supposerons que tous les modèles de calcul sont "équivalents". Cela nous permettra d’utiliser des programmes en Python sans perdre de généralité du propos : ce que ne pourra pas effectuer un programme en python ne sera pas réalisable ici, par un autre type de langage.
C'est cette généralité qui a été présentée par Church dans sa célèbre thèse
Calculabilité
Une fonction calculable désigne une fonction mathématique f pour laquelle il existe un algorithme permettant de trouver f(x) pour tout élément x.
Il va de soit que les fonctions mathématiques que vous connaissez sont toutes calculables :
f(x) = 2x + 3
f(x) = cos (x)
f(x) = log (x)
et il n'est pas facile de trouver une fonction non calculable "simple" bien sûr.
Nous allons donc tenter d'ébaucher un raisonnement nos permettant d'entrevoir l'existence de telles fonctions.
Ensembles infinis dénombrables et non dénombrables
Il existe deux types d'ensembles infinis en mathématiques.
Les ensembles infinis dénombrables
Les ensembles infini non-dénombrables
Pour les ensembles infinis dénombrables, il est possible de "numéroter" chaque élément de l'ensemble.
Par exemple
Les entiers forment un ensemble infini dénombrable, chaque élément est "indexé" par sa valeur (1, 2, 3, 4 ....)
Les nombres réels ne sont pas dénombrables. Si l'on essaye d'indexer tous les réels compris entre 0 et 1, on comprend grâce à l'écriture décimale infinie que cela n'est pas possible
Ensemble des fonctions et des algorithmes
Nous allons nous intéresser à deux ensembles infinis :
L'ensemble des algorithmes
L'ensemble des fonctions
L'ensemble des algorithmes est formé de textes écrits dans un certain alphabet.
Si l'on classe ces algorithmes par ordre alphabétique il est possible de les numéroter. L'ensemble des algorithmes est donc dénombrable.
Il est possible de démontrer que l'ensemble des fonctions n'est pas dénombrable.
De ces deux constations :
L'ensemble des algorithmes est dénombrable
L'ensemble des fonctions est non-dénombrable
On peut conclure qu'il y a "plus" de fonctions que d'algorithmes, et que donc certaines fonctions ne peuvent être calculées par un algorithme.
Décidabilité
Le problème de la décidabilité est proche de celui de la calculabilité. Cette fois on ne va pas chercher à calculer la valeur d'une fonction mais nous allons tenter de répondre à une question par oui ou par non.
S'il n'est pas possible de trouver un algorithme permettant de répondre par oui ou par non à un problème, nos dirons que ce problème est indécidable
Un classique : le problème de l'arrêt
Nous allons enfin pouvoir traiter complètement un problème de décidabilité : le problème de l'arrêt.
Énonce du problème
Une des angoisses des codeurs est de savoir si leur programme ne va pas "tomber" dans une boucle infinie.
Il serait donc judicieux d'ajouter à notre environnement de développement, en plus de l'analyse syntaxique une fonction qui teste notre programme et vérifie s'il ne va pas tourner infiniment.
Le problème de l'arrêt, c'est étudier l'existence d'un algorithme capable de dire si un programme va tourner infiniment ou non.
On pourrait imaginer une technique :
On exécute le programme et on attend assez longtemps....10s ? 1h , 10h, 1 an ? et on regarde si il se termine...
Une telle technique ne va bien sur pas fonctionner, il faudrait plutôt inventer une analyse du texte du programme qui dise si le programme va boucler infiniment ou non ...
Démonstration
Construction
Pour savoir si le problème de l'arrêt est décidable ou non, nous donc allons supposer dans un premier temps qu'une fonction permettant de savoir si un programme va tourner infiniment ou non existe.
Nous allons donc raisonner par l'absurde.
Voici donc notre fonction :
def H(programme,x):
'''
programme est une fonction
x est un paramètre de cette fonction
La fonction H renvoie:
True si programme(x) se termine
False si programme(x) ne se termine pas
'''
...
if ...:
# si le programme s'arrête sur l'entrée x
return True
else:
return False
Comme vous le voyez on a un peu éludé le "cœur" de la fonction H...On verra plus tard.
On remarque ici que programme() est un paramètre de la fonction H.
Un programme peut donc être le paramètre d'une fonction !
Arrivé à ce stade, si l'on veut raisonner par l'absurde, il faut arriver à une situation absurde (logique).
Pour cela nous allons inventer une nouvelle fonction X (entièrement écrite celle là).
def X(programme):
if H(programme,programme)==True:
while True:
continue
else:
return True
Deux questions peuvent apparaître alors :
A quoi sert cette fonction ?
Comment peut-on avoir l'idée de construire une telle fonction ?
Pour la question 1. c'est assez simple,cette fonction a pour seul but de nous faire aboutir à une situation absurde
Pour la question 2. c'est assez simple aussi. En 1936, Alan Turing à fondé cette idée qui a été reprise ensuite et simplifiée. Bref cette fonction est le résultat de réflexions de chercheurs cherchant à résoudre le problème de l'arrêt.
Essayons tout de même d'analyser le code de cette fonction :
La fonction X prend comme paramètre un programme
Elle applique ensuite ce programme à la fonction H à la fois comme programme à analyser et comme paramètre du programme lui même (le x défini dans la fonction H précédente.
Deux cas apparaissent ensuite :
Si H(programme,programme) renvoie True, la fonction X entre dans une boucle infinie :
while True:
Si H(programme,programme) renvoie False, La fonction X se termine en renvoyant True
Raisonnement
Voila, nous sommes arrivés à la fin de notre construction. Il va falloir maintenant l'utiliser ! Nous allons tenter de voir ce que renverrait l'appel suivant de fonction X :
print(X(X))
Commençons donc notre analyse :
L'appel de X(X) va commencer, dès la première ligne, par le test :
if H(X,X):
Bon , nous ne pouvons pas savoir ce qu'il va se passer à ce niveau, mais une chose est sure, la fonction H ne peut renvoyer que deux choses : True ou False. Examinons donc ces deux possibilités :
Si H(X,X) renvoie True :
D'après la définition de la fonction H, cela signifie que X(X) s'est arrêtée.
D'après la définition de la fonction X, on entre dans une boucle infinie. Mais donc X(X) ne s'arrête pas
Si vous relisez bien ces deux conséquences, elles se contredisent !!! C'est absurde !!!
Si H(X,X) renvoie False :
D'après la définition de la fonction H, cela signifie que X(X) ne s'arrêtera t PAS si on l'exécute.
D'après la définition de la fonction X, cela signifie que X(X) va renvoyer True.
Si vous relisez bien ces deux conséquences, elles se contredisent !!! C'est absurde !!!
Dans les deux cas on aboutit à une contradiction. Donc :
Si la fonction X existait...
...On pourrait construire la fonction H...
...Et aboutir à une contradiction
Donc la fonction X ne peut PAS exister
Nous avons réussi à démontrer que le problème de l'arrêt est indécidable !
Si vous voulez suivre à nouveau ce raisonnement, mais de façon plus visuelle, voici une petite vidéo où les fonction sont représentées par des machines qui ressemblent à des imprimantes..
A retenir
Ce chapitre est assez déroutant, pas de technique de codage ici, ou d'algorithme à connaître. Alors que faut-il retenir de tout cela ?
Qu'un ordinateur ne peut pas tout calculer ou tout décider
Que cette limitation est théorique et n'est pas due à des limitations techniques, de langage etc...
Que Church et Turing sont les deux grands noms de la calculabilité et la décidabilité
Que des démonstrations mathématiques complexes sont à la base de la science informatique...même si on ne s'en sert pas tous les jours.