Demandez le programme !
Quelques vidéos pour votre culture :
Alan Turing (1912-1954), sa vie en quelques minutes.
Vous pouvez également visionner le film "imitation game". Synopsis officiel du film : "En 1939, alors que la guerre débute, Turing, déjà reconnu pour ses talents de mathématicien, se rend à Bletchley Park et rejoint l'équipe de cryptographie, sous les ordres du commandant Alastair Denniston. Il est accompagné par le maître d'échecs Hugh Alexander, John Cairncross, Peter Hilton, Keith Furman et Charles Richards. Cette équipe va alors tenter de décrypter la machine de cryptographie utilisée par les nazis : Enigma."
Ce travail secret ne sera connu du public que dans les années 1970. Après la guerre, il travaille sur un des tout premiers ordinateurs, puis contribue au débat sur la possibilité de l'intelligence artificielle, en proposant le test de Turing. Vers la fin de sa vie, il s'intéresse à des modèles de morphogenèse du vivant conduisant aux « structures de Turing ». Poursuivi en justice en 1952 pour homosexualité, il choisit, pour éviter la prison, la castration chimique par prise d'œstrogènes. Il est retrouvé mort par empoisonnement au cyanure le 8 juin 1954 dans la chambre de sa maison à Wilmslow (une pomme croquée posée sur sa table de nuit). La reine Élisabeth II le reconnaît comme héros de guerre et le gracie à titre posthume en 2013.
La pomme croquée d'une célèbre marque est un hommage à ce brillant mathématicien.
Pour les curieux : le coin des machines à calculer mécaniques.
Quelques vidéos :
Une de mes machines (années 60 environ):
Une de mes machines (années 30 environ) :
Une vidéo qui explique toutes les techniques :
Attention cette section 2 sur la machine de Turing hautement culturelle n'est pas un attendu du programme de terminale NSI. Elle peut néanmoins servir de support à un projet.
En 2012 Alan Turing aurait fêté son 100 ième anniversaire. À cette occasion, des universitaires ont construit une machine de Turing en LEGO.
Une machine de Turing est un appareil théorique disposant :
d'une bande infiniment longue ;
une tête qui peut lire et écrire sur cette bande ;
un registre de mémoire (fini) ;
un "processeur" capable de réaliser les opérations suivantes :
déplacer la bande vers la droite / la gauche ;
modifier l'état du registre selon sa valeur actuelle et la valeur sur la bande ;
écrire ou effacer une valeur sur la bande.
Le fonctionnement de la machine atteint (ou pas) un état particulier qui provoque son arrêt. Cet état particulier est souvent appelé état final.
Pour représenter l'ensemble des états, on peut utiliser une table des transitions.
Elle permet de définir, pour chaque état, selon le résultat de la lecture, les actions à exécuter : écriture, déplacement, choix d'un nouvel état.
Pour écrire cette table, il faut :
définir un alphabet utilisé par la machine : c'est la liste des caractères utilisés ;
noter les états sous la forme e0, e1, e2, etc (ou q0, q1, q2, etc) ;
représenter l'état final par qf (ou ef) mais parfois indiqué par une chaine de caractères ou un caractère particulier ("final" ou □).
Dans certains exercices, on déplace le ruban mais pas la tête de lecture/écriture.
Alphabet : {'0', '1', 'b'}, 'b' pour un "blanc", c'est-à-dire une case du ruban sans "0" ni "1".
Etat | Lit | Ecrit | Déplace | Suivant |
---|---|---|---|---|
e0 | blanc | rien | droite | e0 |
À l'état e0 si la tête lit un blanc, elle n'écrit rien, se déplace à droite et reste à l'état e0
Cette table des transitions donnait naissance à une carte percée de trous que l'on pouvait insérer dans la machine. À l'aide d'aiguilles et de relais électro-mécaniques, ces informations provoquaient des actions dans la machine. Ces cartes étaient les programmes de la machine. Par contre Alan Turing ne verra pas sa "machine" fonctionner.
On peut construire un diagramme de la machine (on parle aussi d' automate).
Pour définir un diagramme, il faut :
un alphabet utilisé par la machine : liste des caractères utilisés, comme par exemple {'0', '1', 'b'}.
des états sont symbolisés par des cercles et écrits sous la forme q1, q2, etc (ou e1, e2, etc).
un état final représenté par ef (ou qf) mais parfois indiqué par une chaine de caractères ou un caractère particulier ("final" ou □).
représenter les déplacements par les lettres L
et R
(left et right).
utiliser des triplets (lecture ; écriture ; déplacement)
et des flèches qui indiquent l'état
suivant.
Alphabet : {'0', '1', 'b'}
À l'état e0, si la tête lit un blanc alors elle n'écrit rien et se déplace à droite en restant à l'état e0.
Alphabet : {'0', '1', 'b'}
À l'état e1, si la tête lit un 0 alors elle écrit 1, se déplace à droite et reste à l'état e1.
À l'état e1, si la tête lit un 1 alors elle écrit 0, se déplace à droite et reste à l'état e1.
À l'état e1, si la tête lit un blanc alors elle n'écrit rien et passe à l'état ef (elle s'arrête).
Alphabet : {'0';'1;'b'}
si la tête lit un blanc alors elle n'écrit rien et se déplace à droite en restant à l'état e0.
À l'état e0, si la tête lit un 0 alors elle n'écrit rien, ne se déplace pas et passe à l'état e1.
À l'état e0, si la tête lit un 1 alors elle n'écrit rien, ne se déplace pas et passe à l'état e1.
À l'état e1, si la tête lit un 0 alors elle écrit 1, se déplace à droite et reste à l'état e1.
À l'état e1, si la tête lit un 1 alors elle écrit 0, se déplace à droite et reste à l'état e1.
Un diagramme de la machine est un graphe orienté dont les sommets sont les états et les arcs représentent les passages possibles d'un état à un autre.
Dans cet exemple, on écrit une machine de Turing qui remplace les 0 par des 1 et les 1 par des 0.
Analyse du problème et liste des états à écrire.
Un état e0 qui lit des caractères "blancs" et qui avance de gauche à droite.
Un état e1 qui lit, remplace les 0 par des 1 (et réciproquement) et qui avance de gauche à droite.
Si l'état e1 lit un blanc, on passe à l'état final ef.
un état final ef qui arrête la machine.
Cela peut donner le diagramme suivant :
Réalisons la table des transitions de l'exemple précédent.
Etat | Lit | Ecrit | Déplace | Suivant |
---|---|---|---|---|
e0 | blanc | rien | droite | e0 |
0 | rien | rien | e1 | |
1 | rien | rien | e1 | |
e1 | 0 | 1 | droite | e1 |
1 | 0 | droite | e1 | |
b | rien | rien | ef | |
ef | Arrêt de la machine |
D'un point de vue machine, cela donnerait les représentations suivantes :
État initial : État final :
Écrire une machine de Turing qui remplace les 1 par des 0 et qui laisse les 0. Cette machine doit transformer le nombre 01010001 par 00000000.
D'un point de vue machine, cela donnerait les représentations suivantes :
État initial : État final :
Vous écrirez le diagramme d'états ainsi que la table des transitions.
Analyse du problème et liste des états à écrire.
Un état e0 qui lit des caractères "blancs" et qui avance de gauche à droite.
Un état e1 qui lit, remplace les 0 par des 1 (et réciproquement) et qui avance de gauche à droite.
Si l'état e1 lit un blanc, on passe à l'état final ef.
un état final ef qui arrête la machine.
Que produit la machine de Turing décrite par le diagramme suivant en partant d'un ruban vide ?
Un simulateur de machine de Turing (attention c'est le ruban qui se déplace ):
Source : INRIA-interstices-machine de Turing
Explications sur le simulateurUn autre simulateur du site de M. PECHAUD (professeur en classe préparatoire)
Quelques liens et ressources :
Il est important de comprendre qu'un programme utilise des données sur lesquelles il travaille.
Ces données peuvent être également des programmes.
Le compilateur Python (ou les autres langages) est un programme (Edupython, Jupyter, idle de python, etc) qui utilise votre programme comme une donnée.
Voici par exemple le contenu d'un programme écrit en langage Python stocké dans un fichier nommé exemple_programme.py
Pour exécuter ce programme, il vous suffit :
Sur Edupython, de saisir ce contenu dans la partie édition, puis d'appuyer sur la flèche verte pour exécuter le programme.
Ce programme est alors pris en charge, en tant que données, par l'interpréteur Python, interpréteur qui renverra la réponse affichée.
Sur VSCode, de saisir ce contenu puis d'exécuter dans le terminal ce code. Là encore, le programme est une donnée prise en charge par l'interpréteur python.
Voici d'autres exemples dans lesquels un programme est une donnée d'un autre programme :
Tous les systèmes d'exploitation (Windows, Linux, Unix, MacOS, Android) sont des programmes gigantesques qui utilisent et font trourner des programmes, appelées souvent applications, comme données.
Le téléchargement de logiciels : tout logiciel est un programme. Le gestionnaire de téléchargement prend comme donnée le programme à télécharger.
Un débuggeur : pour repérer une éventuelle erreur dans le code source d'un programme, l'éditeur de programmation exécute un débugger qui prend comme donnée le programme à tester.
Une fonction calculable est une fonction que l'on peut écrire sous forme d'algorithme.
Les machines de Turing sont une réponse à la recherche de définition d'une fonction calculable.
Toute fonction calculable est calculable grâce à une machine de Turing. C'est la thèse de Church : "Toute fonction physiquement calculable est calculable par une machine de Turing".
Les fonctions calculables par d'autres modèles de calculs suffisamment riches (machines à registres, machine RAM, lambda calcul...) sont les mêmes que les fonctions calculables par des machines de Turing.
Il existe même une machine de Turing universelle qui simule toutes les autres machines de Turing. Les machines de Turing sont en fait des modèles universels de calcul.
La calculabilité ne dépend pas du langage utilisé.
Ainsi, si une fonction est calculable, il existe un algorithme dans le langage que vous utilisez qui permet de la calculer.
Écrire la fonction successeur
qui prend en paramètre un entier et qui renvoie son successeur en langage Python.
Écrire la fonction successeur
qui prend en paramètre un entier et qui renvoie son successeur en langage JavaScript.
Écrire cette fonction à l'aide d'une machine de Turing, c'est-à-dire doner la table des transitions.
Fonctions non-calculables : contrairement à ce que l'on pourrait imaginer, il existe des fonctions qui ne sont pas calculables. Il y en a même une infinité "plus grande" que de fonctions calculables.
La complexité de Kolmogorov est un exemple de fonction non-calculable.
La complexité de Kolmogorov est une fonction qui à tout objet associe la longueur du plus petit algorithme qui permet d'écrire ce nombre dans un language donné.
"Bac!Bac!Bac!Bac!Bac!Bac!Bac!Bac!Bac!Bac!" peut être écrit en langage Python au moins de trois façons suivantes :
directement comme "Bac!Bac!Bac!Bac!Bac!Bac!Bac!Bac!Bac!Bac!"
avec la fonction f suivant :
def f():
return "Bac!"*10
avec la fonction f anonymisée :
f=lambda:"Bac!"*10
Le dernier programme est celui nécessitant le moins de caractères (17), donc la complexité de l'objet "Bac!Bac!Bac!Bac!Bac!Bac!Bac!Bac!Bac!Bac!" est d'au plus 17. (Il est peut-être possible d'écrire cet objet avec un code encore plus condensé en langage Python, d'où le "au plus").
Par contre une chaîne complètement aléatoire comme "a7G$ke5yTC/sw2J!O1f8MnGU6m.r?q" ne peut pas être
déterminée par un algorithme de taille inférieure à la longueur de la chaîne.
Sa complexité de Kolmogorov est au moins celle de sa longueur.
Prouvons, par un raisonnement par l'absurde, que la complexité de Kolmogorov est non-calculable.
Supposons que celle complexité soit calculable. Il existe donc une fonction kolmogorov
qui à tout objet obj
renvoie sa complexité de Kolmogorov.
Notons carac
le nombre de caractères nécessaires pour écrire cette fonction kolmogorov
.
Considérons cette fonction seuil_kolmogorov
:
def seuil_kolmogorov():
n = 0
while kolmogorov(n) < carac + 1000:
n += 1
return n
Cette fonction seuil_kolmogorov
écrite en langage Python renvoie le plus petit entier naturel dont sa complexité de
Kolmogorov dépasse ou égale $carac + 1000$.
Notons $min$ cet entier.
Un tel entier existe forcément (d'où la terminaison du programme) car :
Il existe une infinité de nombres entiers,
Mais il n'existe qu'un nombre fini d'algorithmes que l'on peut écrire avec au plus $carac + 1000$ caractères (au plus $nombre\_caractère^{carac + 1000}$) donc a fortiori il existe un nombre fini d'algorithmes écrit avec au plus $carac + 1000$ caractères donnant comme résultat un nombre entier.
Cette fonction seuil_kolmogorov
est un algorithme qui renvoie l'entier $min$ et qui
s'écrit avec moins de 1000 caractères donc strictement moins que $carac + 1000$. Par définition de la complexité de
Kolmogorov, kolmogorov(min)
$\lt 1000$.
Cependant, par définition de $min$, $min$ est un nombre entier tel que kolmogorov(min)
$\ge carac + 1000$,
c'est même le plus plus petit entier vérifiant cela.
Ainsi, on a à la fois :
kolmogorov(min)
$\lt 1000$.
kolmogorov(min)
$\ge carac + 1000 \gt 1000$.
On aboutit à une contradiction absurde.
Cela signifie que l'hypothèse initiale "la complexite de Kolmogorov est calculable" est fausse.
On a prouvé par un raisonnement par l'absurde que la complexité de Kolmogorov est une fonction non calculable.
Les curieux.ses peuvent aussi aller voir la notion de fonctions des castors affairés.
Une propriété est décidable s'il existe un algorithme qui la confirme ou l'infirme en un nombre fini d'étapes. L'algorithme doit répondre par oui ou par non à la question posée par le problème. On parle dans ce cas de problème de décision.
Il existe des propriétés non décidables.
Il nous est arrivé à tous d'écrire une boucle infinie. C'est le drame car notre programme ne s'arrête jamais. Dans certains domaines, ce problème devient un problème essentiel. Pensez aux problèmes de sécurité (centrale nucléaire, aviation, transports, etc).
Il est donc naturel de se poser cette question :
Peut-on écrire un algorithme capable de prédire que n'importe quel programme va s'arrêter ? La réponse est non. On dit que le problème de l'arrêt d'un programme est indécidable.
Des exemples de programmes qui ne s'arrêtent pas.
Que pensez-vous du script suivant ?
def est_pair(n:int)->bool :
"""Renvoie true si n est pair"""
assert(isinstance(n, int)) # Test si n est entier
test = False
if n%2 == 0 : test = True
return test
def infini(n: int) :
while est_pair(n):
n = n*2
return n
print(infini(5))
print(infini(4))
Vous pouvez si besoin tester ce code dans ce trinket
Inventer une boucle infinie qui ne soit pas triviale.
Le raisonnement par l'absurde est une manière de raisonner qui consiste à démontrer
la vérité d'une proposition en prouvant l'absurdité du contraire de celle-ci.
Synonyme : apagogie.
Lorsque l'on cherche à démontrer une propriété par l'absurde, on prend comme hypothèse la proposition contraire et on en déduit une (ou des) conséquence(s) absurdes.
Il existe de nombreux exemples en mathématiques de raisonnement par l'absurde :
Le nombre 0 n'a pas d'inverse.
$\sqrt{2}$ est un nombre irrationnel (il ne peut pas s'écrire sous la forme d'une fraction). C'est au programme de seconde en mathématiques.
Il y a une infinité de nombres premiers.
Pour ceux et celles voulant développer ou mieux comprendre le raisonnement par l'asurde, il vous suffit de consulter ce cours de maths expertes.
Nous allons utiliser ce moyen pour démontrer la propriété suivante :
Le problème de l'arrêt d'un programme est indécidable.
En voici une démonstration à comprendre et connaître :
Raisonnons par l'absurde.
Dans un premier temps, on fait l'hypothèse que l'on est capable de construire un algorithme nommé
halt
qui résout le problème de l'arrêt d'un programme.
L'objectif est de démontrer que cette hypothèse est fausse.
En pseudo-langage, cette fonction halt
peut être vue ainsi :
fonction halt(P : programme) : booléen si P s'arrête alors on renvoie vrai sinon on renvoie faux
On construit maintenant un deuxième algorithme que l'on appelle contradiction
qui :
exécute l'algorithme halt
,
renvoie le mot "stop" si l'exécution de halt
renvoie faux,
exécute une boucle infinie si l'exécution de halt
renvoie vrai.
procédure contradiction(P : programme) test←halt(P) si test=vrai alors on exécute une boucle infinie sinon on affiche "stop"
On applique maintenant la procédure contradiction
à elle même. On exécute ainsi
contradiction(contradiction)
.
On appelle halt(contradiction)
.
Deux cas possibles :
Soit le programme contradiction
s'arrête alors halt(contradiction)
renvoie vrai. Mais dans ce cas le programme contradiction
ne s'arrête pas car on exécute une boucle
infinie.
Soit le programme contradiction
ne s'arrête pas alors
halt(contradiction)
renvoie faux. Mais dans ce cas le programme contradiction
s'arrête en affichant "stop".
On obtient donc une contradiction : contradiction(contradiction)
aboutit à deux cas contradictoires.
Par ce raisonnement par l'absurde, notre hypothèse de départ : "on est capable de construire un algorithme halt
qui résout le problème de l'arrêt d'un programme" est fausse.
Nous venons donc de démontrer que le programme de l'arrêt est indécidable.
Vous retrouverez cette démonstration dans cette vidéo.
Pour les curieux : la notion en logique de paradoxe.
Intéressez vous à la proposition suivante : "je suis un menteur".
Considérons que ce que je dis est vrai : je suis un menteur. Mais étant que menteur, si je dis que je suis un menteur alors ce que je dis est faux, donc je ne mens pas. Mais j'ai donc menti en disant que je suis un menteur.
Maintenant, considérons que ce que je dis est faux, c'est-à-dire qu'en fait je ne suis pas menteur.
Comme je ne mens pas alors ce que je dit est vrai, en particulier mon affirmation "je suis un menteur".
Je dis donc le contraire de la vérité donc je mens, ce qui contredit mon hypothèse de départ.
Dans les deux cas j'aboutit à une absurdité ! Cette proposition est donc ni vraie, ni fausse : quel paradoxe !
Ce paradoxe du menteur serait attribué à Epiménide le Crétois (VIIème sicle av J-C).
On parle dans ce cas d'autoréférence, c'est-à-dire une phrase ou une formule qui fait
référence à elle même.
Cette notion a beaucoup d'applications en mathématiques, philosophie et en programmation.
Un énoncé contenant une autoréférence est parfois paradoxal.
En 1931, Kurt Gödel, pour démontrer son théorème d'incomplétude, utilise un énoncé inspiré de ce paradoxe.
Vous pouvez vous intéresser aux problèmes du pavage avec les tuiles de Wang (1961) qui constitue une méthode visuelle de pavage pour représenter la notion de calculabilité. Faire une recherche sur le sujet. Accès à une revue sesamath sur le sujet
Dans le cadre d'un projet, vous pouvez programmer en langage python un simulateur de machine de Turing. Vous pourrez développer ce simulateur en programmation orientée objet.