Alan Turing est souvent vu comme le père de l'informatique.
Quelques dates clés :
Tout programme peut être vu comme une donnée :
Cette précision est là pour bien mettre en avant que de nos jours un algorithme est programmable et n'est donc pas induit dans la conception de la machine elle-même. On fait ainsi la différence, par exemple, entre une horloge mécanique et une montre programmée informatiquement.
Une machine de Turing est une machine conceptualisée par Alan Turing. C'est une machine imaginaire que l'on peut représenter ainsi :
À faire dans le cahier.
On possède la table de transition suivante.
| État | Valeur lue | Écriture | Direction | Nouvel état |
|---|---|---|---|---|
| 0 | vide | vide | gauche | 1 |
| 0 | 0 | 0 | droite | 0 |
| 0 | 1 | 1 | droite | 0 |
| 0 | 2 | 2 | droite | 0 |
| 0 | 3 | 3 | droite | 0 |
| 0 | 4 | 4 | droite | 0 |
| 0 | 5 | 5 | droite | 0 |
| 0 | 6 | 6 | droite | 0 |
| 0 | 7 | 7 | droite | 0 |
| 0 | 8 | 8 | droite | 0 |
| 0 | 9 | 9 | droite | 0 |
| 1 | 0 | 1 | gauche | 2 |
| 1 | 1 | 2 | gauche | 2 |
| 1 | 2 | 3 | gauche | 2 |
| 1 | 3 | 4 | gauche | 2 |
| 1 | 4 | 5 | gauche | 2 |
| 1 | 5 | 6 | gauche | 2 |
| 1 | 6 | 7 | gauche | 2 |
| 1 | 7 | 8 | gauche | 2 |
| 1 | 8 | 9 | gauche | 2 |
| 1 | 9 | 0 | gauche | 1 |
| 1 | Vide | 1 | STOP | |
| 2 | 0 | 0 | gauche | 2 |
| 2 | 1 | 1 | gauche | 2 |
| 2 | 2 | 2 | gauche | 2 |
| 2 | 3 | 3 | gauche | 2 |
| 2 | 4 | 4 | gauche | 2 |
| 2 | 5 | 5 | gauche | 2 |
| 2 | 6 | 6 | gauche | 2 |
| 2 | 7 | 7 | gauche | 2 |
| 2 | 8 | 8 | gauche | 2 |
| 2 | 9 | 9 | gauche | 2 |
| 2 | Vide | Vide | STOP |
Appliquer la table de transition ci-dessus sur le ruban suivant :
| 2 | 3 | 5 |
L'initialisation se fait sur la case 2 avec pour état 0.
À faire dans le cahier.
Refaire l'exercice 4 avec le ruban suivant :
| 2 | 9 | 9 |
L'initialisation se fait sur la case 2 avec pour état 0.
Refaire l'exercice 4 avec le ruban suivant :
À faire dans le cahier.
| 9 | 9 | 9 |
L'initialisation se fait sur le tout premier 9 avec pour état 0.
À faire dans le cahier.
On considère le ruban suivant :
| 1 | 1 | 0 | 1 | 1 |
Écrire une table de transition permettant de remplacer le 0 par un 1.
L'initialisation se fait sur la case vide la plus à gauche. Le programme s'arrêtera sur cette même case.
À faire dans le cahier.
On considère le ruban suivant :
| 1 | 1 | 0 | 1 | 1 |
Écrire une table de transition permettant de remplacer le 0 par un 1 et réciproquement puis de rajouter un 1 à l'avant-dernière case.
L'initialisation se fait sur le tout premier 1 et le programme stoppera sur la case vide la plus à droite.
Dans les années 1930, les travaux des mathématiciens Alonzo Church et Alan Turing ont permis d'éclaircir ce qu'est un programme basé sur des calculs.
Chacun a proposé sa propre définition de ce que veut dire « calculer » : le lambda-calcul pour Church, la machine de Turing (vue ci-dessus) pour Turing. On a ensuite démontré que ces deux définitions, pourtant très différentes, décrivent exactement les mêmes fonctions calculables. Ce constat a conduit à la thèse suivante :
Tout traitement réalisable mécaniquement peut être accompli par une machine de Turing.
Attention : il s'agit bien d'une thèse et non d'un théorème. On ne peut pas la démontrer, car elle relie une notion intuitive (« ce qu'un humain ou une machine peut calculer en suivant une méthode mécanique ») à une définition mathématique précise (la machine de Turing). Elle n'a jamais été mise en défaut : tous les modèles de calcul proposés depuis, y compris nos ordinateurs actuels, calculent exactement les mêmes fonctions qu'une machine de Turing.
En informatique, la calculabilité étudie ce qu’un ordinateur peut ou ne peut pas résoudre à l’aide d’un programme.
Un problème est dit calculable s’il existe un algorithme qui fournit une réponse correcte et qui termine (s'arrête au bout d'un nombre fini d'étapes) pour toute entrée possible.
Un problème est dit non calculable lorsqu’aucun algorithme ne peut le résoudre dans tous les cas possibles.
La calculabilité ne dépend ni de la puissance de l’ordinateur, ni du langage de programmation utilisé : un problème non calculable le reste quelle que soit la machine. Il s'agit d'une limite théorique, et non d'un manque de moyens techniques.
En informatique, un problème est dit décidable s’il existe un algorithme qui donne une réponse oui ou non pour toutes les entrées possibles, et qui se termine toujours.
Un problème est décidable lorsqu’un algorithme permet de répondre correctement dans tous les cas, sans jamais boucler indéfiniment.
Un problème est indécidable lorsqu’aucun algorithme ne peut répondre correctement pour toutes les entrées tout en garantissant de toujours s’arrêter.
La décidabilité est le cas particulier de la calculabilité appliqué aux problèmes à réponse oui / non. Pour un tel problème, être décidable et être calculable sont donc la même chose.
La calculabilité est la notion la plus large : elle concerne aussi des problèmes dont la réponse n'est pas un oui ou un non, comme trier une liste ou calculer un PGCD.
Le problème de l’arrêt consiste à savoir si un programme s’arrêtera ou non pour une entrée donnée. C'est un problème à réponse oui / non. Nous allons démontrer dans la partie suivante qu'il est indécidable : aucun algorithme ne peut toujours donner la bonne réponse.
On peut se demander s'il est possible de prévoir qu'un algorithme s'arrêtera.
Est-il possible d'écrire un algorithme permettant de savoir si n'importe quel autre algorithme s'arrête ou non ?
Raisonnons par l'absurde :
Imaginons que ce programme, que l'on appellera \(A\), existe.
On lui donne deux choses : un programme \(P\) et l'entrée \(e\) sur laquelle on veut l'exécuter. Alors \(A(P,e)=VRAI\) si \(P\) s'arrête sur l'entrée \(e\), et \(A(P,e)=FAUX\) si \(P\) ne s'arrête pas sur cette entrée.
Rappelons-nous qu'un programme est une donnée (partie 1) : on peut donc donner un programme à lui-même comme entrée.
Que se passe-t-il si le texte reçu n'a pas de sens pour \(P\) ? \(P\) peut planter : une erreur compte
comme un arrêt. Il peut aussi ignorer son entrée, ou boucler. Dans tous les cas, soit il s'arrête, soit il ne
s'arrête pas : \(A(P,P)\) a donc bien une réponse, VRAI ou FAUX, pour tout programme
\(P\).
Construisons maintenant un programme \(B\) défini de la manière suivante :
VRAI, il lance une boucle infinie.FAUX, il s'arrête.Appliquons enfin \(B\) à lui-même et regardons \(B(B)\) :
FAUX, c'est-à-dire que \(B\) ne s'arrête pas sur
l'entrée \(B\) : contradiction.VRAI, c'est-à-dire que \(B\) s'arrête sur
l'entrée \(B\) : contradiction également.Les deux cas sont impossibles, donc le programme \(B\) ne peut pas exister. Or, si \(A\) existait, il suffirait de quelques lignes pour construire \(B\) à partir de lui. C'est donc que \(A\) n'existe pas : le problème de l'arrêt est indécidable.
Ces deux problèmes attendent une réponse par oui ou par non : ils sont donc indécidables.
Étant donné un programme et une entrée, déterminer avec certitude si ce programme va s’arrêter ou tourner indéfiniment. C'est le problème de l'arrêt : nous venons de démontrer qu'aucun algorithme ne peut répondre correctement dans tous les cas.
Écrire un programme capable d’identifier avec certitude tous les programmes malveillants possibles. Ce problème est non calculable, et la raison n'est pas que les virus seraient bien cachés : on démontre que si un tel détecteur existait, on pourrait s'en servir pour résoudre le problème de l'arrêt, dont on vient de démontrer qu'il n'a pas de solution. C'est pourquoi les antivirus réels se contentent de reconnaître des virus déjà connus ou des comportements suspects, sans jamais pouvoir tous les détecter.
Le problème de l'arrêt montre qu'aucun programme ne pourra jamais décider à notre place si n'importe quel algorithme s'arrête. Pour un algorithme donné, on peut en revanche souvent le prouver à la main : c'est l'objet de cette partie.
Un algorithme est dit terminant s’il s’arrête après un nombre fini d’étapes, quelle que soit l’entrée fournie.
Un algorithme qui ne termine pas ne donne jamais de résultat. Avant de se demander si le résultat est le bon (partie suivante), il faut donc s'assurer que l'algorithme termine.
Pour prouver qu’un algorithme termine, on montre qu’à chaque étape une quantité diminue strictement et qu’elle ne peut pas diminuer indéfiniment.
def compte_a_rebours(n):
while n > 0:
n = n - 1
Ici, la quantité qui décroît (on l'appelle le variant de la boucle) est la variable
n : c'est un entier qui diminue strictement à chaque tour, et la condition n > 0
garantit qu'il reste positif tant que la boucle tourne. Une suite d'entiers positifs strictement décroissante ne
peut pas être infinie : la boucle termine donc forcément.
À faire dans le cahier.
On considère l’algorithme suivant :
def mystere(n):
while n != 1:
if n % 2 == 0:
n = n // 2
else:
n = n + 1
return n
On suppose que n est un entier strictement positif.
Questions :
n est pair.n est impair.n ? Justifier.Un algorithme est dit correct s’il produit toujours le résultat attendu pour toutes les entrées valides.
Prouver la correction d’un algorithme consiste à montrer, de manière logique et rigoureuse, que l’algorithme fait exactement ce qui est demandé dans l’énoncé.
La correction totale s'obtient donc en ajoutant à la correction partielle une preuve de terminaison (partie précédente).
Pour les algorithmes utilisant des boucles, on utilise souvent un invariant de boucle.
Un invariant est une propriété qui est vraie :
Algorithme : calculer la somme des éléments d’une liste.
somme = 0
pour chaque element de la liste :
somme = somme + element
Invariant : après chaque itération, somme est égale à la somme des éléments déjà parcourus.
À la fin de la boucle, tous les éléments ont été parcourus, donc somme est bien la somme totale.
À faire dans le cahier.
On considère l’algorithme suivant, écrit en Python :
def maximum(liste):
max_val = liste[0]
for x in liste:
if x > max_val:
max_val = x
return max_val
On suppose que liste est une liste non vide de nombres.
max_val dans cet algorithme ?max_val.