Essayez

Lancez la machine pour la voir à l’œuvre. À tout moment, vous pouvez avancer pas à pas ou mettre en pause pour regarder de plus près, ou réinitialiser pour recommencer.

Plus d’une douzaine de machines d’exemple sont à explorer.

La plupart des exemples acceptent une entrée. Essayez différentes entrées pour voir ce qui se passe ! Modifiez le code et cliquez sur Charger la machine pour synchroniser vos changements.

Que se passe-t-il ?

Les cercles colorés sont les états. Les carrés en dessous sont les cellules du ruban.

L’état courant et la cellule courante sont mis en évidence.

À chaque étape, une machine de Turing lit son état courant et le symbole sous la tête de lecture, puis les cherche dans sa table de transitions pour y trouver une instruction. Chaque instruction fait trois choses :

  1. écrire un symbole dans la cellule courante
  2. se déplacer d’une cellule vers la gauche ou vers la droite
  3. définir le nouvel état

C’est tout !

Cela se répète étape après étape, jusqu’à ce que la machine atteigne une combinaison d’état et de symbole pour laquelle aucune instruction n’est définie. À ce moment-là, elle s’arrête.

Une machine de Turing est un dispositif abstrait qui modélise le calcul comme une manipulation mécanique de symboles.

Chaque machine possède un nombre fini d’états et un nombre fini de symboles possibles. Ceux-ci sont fixés avant le démarrage de la machine et ne changent pas pendant son exécution.

Le ruban, en revanche, comporte une infinité de cellules, s’étendant sans fin vers la gauche et vers la droite. Chaque cellule porte un symbole. Toute cellule ne faisant pas partie de l’entrée, ou qui n’a pas encore été écrite, porte par défaut le symbole vide. Remarquez qu’à chaque étape, seul un nombre fini de cellules porte un symbole non vide.

(En tant que modèle mathématique, une machine de Turing dispose d’une mémoire infinie - un ruban infini - afin de ne pas restreindre artificiellement sa puissance. En pratique, beaucoup de machines intéressantes n’utilisent qu’une mémoire finie et peuvent être entièrement simulées pour des tailles d’entrée raisonnables. Même les machines qui utilisent une mémoire infinie - et ne s’arrêtent donc jamais - n’emploient au plus qu’une nouvelle cellule par étape, et peuvent donc être simulées dans une certaine mesure.)

La définition formelle d’une machine de Turing connaît de légères variantes, mais il s’agit essentiellement d’un tuple (une liste ordonnée) comprenant

  • les états Q
  • l’état initial q₀ ∈ Q
  • l’alphabet d’entrée Σ
  • l’alphabet de ruban Γ, où Σ ⊆ Γ
  • le symbole vide b ∈ Γ
  • la fonction de transition δ, de type Q × Γ → Γ × {L, R} × Q

où Q, Σ et Γ sont des ensembles finis non vides. Certaines définitions exigent en outre que le symbole vide ne fasse pas partie de l’entrée (b ∉ Σ).

À titre d’exemple, voici une description formelle de la machine « incrémentation binaire » :

Q = { right, carry, done }
q₀ = right
Σ = { 1, 0 }
Γ = { 1, 0, ' ' }
b = ' '

δ(right, 1) = (1, R, right)
δ(right, 0) = (0, R, right)
δ(right, ' ') = (' ', L, carry)
δ(carry, 1) = (0, L, carry)
δ(carry, 0) = (1, L, done)
δ(carry, ' ') = (1, L, done)

Notez que, par souci de simplicité, le simulateur limite chaque symbole à un seul caractère. De plus, l’entrée n’est pas vérifiée par rapport à un alphabet d’entrée, ce qui évite d’avoir à définir les alphabets d’entrée et de ruban.

(Le comportement d’une machine de Turing peut également être décrit en termes mathématiques. Sans entrer dans les détails, cela consiste à définir comment une configuration de la machine - son état, le contenu du ruban et la position de la tête de lecture (la cellule courante) - mène à la configuration suivante, à partir de la fonction de transition. Pour commencer, le contenu du ruban peut être défini comme une fonction des entiers vers les symboles, la position de la tête étant un entier, et chaque déplacement {L, R} ajoutant respectivement -1 ou +1 à cette position.)

Certains aspects de la définition varient d’un auteur à l’autre, mais ces différences relèvent de la préférence et n’affectent pas la puissance de calcul. Autrement dit, les machines d’un modèle peuvent être simulées ou converties pour s’exécuter sur un autre modèle.

Voici quelques-unes des variantes que vous pourrez rencontrer :

  1. Le ruban ne s’étend à l’infini que vers la droite. L’extrémité gauche est le point de départ de la tête de lecture. Un déplacement vers la gauche à cette extrémité laisse la tête sur la même cellule.
  2. Outre « aller à gauche » (L) et « aller à droite » (R), un déplacement de la tête peut être « ne pas bouger » (N).
  3. Au lieu de déplacer la tête de lecture, le déplacement décale le ruban lui-même, de sorte que les directions L et R sont inversées. (Décaler le ruban vers la gauche revient à déplacer la tête vers la droite.)
  4. La machine ne peut s’arrêter que dans l’un de deux états : un état désigné comme état d’acceptation, ou un autre désigné comme état de rejet. Ces états n’ont pas de transition sortante. Tous les autres états doivent définir une transition pour chaque symbole.

Le simulateur a été conçu en tenant compte de ces considérations, entre autres.

  1. Limiter le ruban s’est révélé peu pratique : il fallait souvent marquer l’extrémité gauche par un symbole et écrire les nombres à l’envers. À l’inverse, les machines conçues pour un ruban infini à droite fonctionnent sur un ruban doublement infini avec peu ou pas de modification.
  2. Comparé à L et R, « ne pas bouger » (N) semble rarement utilisé. Il a été omis pour l’instant, par souci de simplicité conceptuelle.
  3. Il est plus intuitif de définir la cellule courante directement en déplaçant la tête de lecture. Turing suit également cette convention. (La visualisation reste toutefois centrée sur la tête, pour éviter qu’elle ne sorte du champ.)
  4. La convention acceptation/rejet reste utilisable ; elle n’est simplement pas obligatoire. Le simulateur accepte d’ailleurs un raccourci commode, illustré par certains exemples : omettre l’état de rejet et toutes les transitions qui y mènent, et traiter un arrêt sur un état non acceptant comme une transition de rejet implicite. Cela réduit le code répétitif et donne un diagramme plus lisible.

Pour une introduction très accessible aux machines de Turing, y compris leur portée et leurs implications, voyez l’excellent article de la Stanford Encyclopedia of Philosophy (en anglais).

Créez la vôtre

Dérivez vos propres versions des exemples, ou de vos créations, avec Édition > Dupliquer le document. Vous pouvez aussi partir de zéro avec un nouveau document vierge.

Décrire une machine de Turing ne demande qu’un état initial, un symbole vide et une table de transitions.

Exemple

# Ajoute 1 à un nombre binaire.
input: '1011'
blank: ' '
start state: right
table:
  # aller jusqu'au chiffre le plus à droite
  right:
    1: R
    0: R
    ' ': {L: carry}
  # puis propager la retenue
  carry:
    1: {write: 0, L}
    0: {write: 1, L: done}
    ' ': {write: 1, L: done}
  done:

Ici, les états sont right, carry et done.
Les symboles sont « 1 », « 0 » et « ».

Nous désignons un état comme état initial (start state), et un symbole comme symbole vide (blank), présent sur les cellules non marquées.

Un état et un symbole, ensemble, correspondent à une instruction. Dans l’état carry, par exemple, le symbole 1 correspond à l’instruction {write: 0, L}. Lorsqu’aucune instruction n’est définie, comme dans l’état done, la machine s’arrête.

Format d’une instruction

La forme générale d’une instruction comporte trois parties :
{write: symbole, déplacement: état}
  • {write: 1, L: done} écrit le symbole 1, déplace la tête de lecture vers la gauche (L) et passe à l’état done.

Par concision, vous pouvez omettre le symbole et l’état s’ils restent inchangés.

  • {L: carry} réécrit le symbole qui vient d’être lu, déplace la tête vers la gauche et passe à l’état carry.
  • R (raccourci pour {R}) déplace simplement la tête vers la droite. Le même symbole est réécrit et l’état reste inchangé.

Astuces

Raccourcis clavier de l’éditeur

D’autres raccourcis clavier figurent dans la liste complète.

Astuces diverses

  • Faites glisser ou cliquez sur un nœud d’état pour le fixer ; double-cliquez pour le libérer.
  • N’hésitez pas à dupliquer. Utilisez Édition > Dupliquer le document pour créer un instantané de votre document courant chaque fois que vous vous apprêtez à faire une modification importante.
  • Les modifications sont enregistrées automatiquement dans le stockage local de votre navigateur. Elles y restent d’une session à l’autre, mais soyez prudent en effaçant les données de navigation. Pour conserver une machine, copiez son code dans un fichier texte.
  • La navigation privée utilise un stockage temporaire distinct. C’est pratique pour essayer des modifications sans les conserver.

Le code est écrit en YAML 1.2, un format de données polyvalent.

Si vous connaissez JSON, YAML lui ressemble, mais il est conçu pour être plus lisible. Les associations peuvent utiliser l’indentation au lieu des accolades {}, et les chaînes peuvent souvent être écrites directement, sans guillemets.

Certaines chaînes doivent être mises entre guillemets : par exemple celles qui commencent ou finissent par une espace, ou qui contiennent certains caractères ayant une signification particulière en YAML. Si une chaîne pose problème, essayez de l’entourer de guillemets.