ATLAS Ouvrir dans l'atlas →

Théorème 1Calculabilité

Les fonctions récursives sont calculables par machine

Énoncé

Toute fonction récursive f:ℕn⇀ℕf : {\mathbb N^n} \rightharpoonup \mathbb N est calculable par une machine à registres : il existe un programme PP tel que f(x)≃fP(n)(x)f(x) \simeq f_P^{(n)}(x) pour tout xx.

Démonstration

Trois faits. Disons qu'un programme mentionne les registres qui figurent dans ses instructions.

(a) Localité. Un programme ne modifie que les registres qu'il mentionne, et son calcul ne dépend que de leur contenu : c'est vrai d'une étape, donc de toutes, par récurrence.

(b) Renommage. Si σ\sigma est une injection de ℕ\mathbb N dans ℕ\mathbb N, le programme PσP^\sigma, où chaque registre ii est remplacé par σ(i)\sigma(i), fait sur les registres σ(i)\sigma(i) exactement ce que PP fait sur les registres ii.

(c) Enchaînement. Soit PP et QQ de longueurs LL et L′L'. Le programme P;QP ; Q s'obtient en remplaçant, dans PP, tout saut vers un numéro j⩾Lj \geqslant L par un saut vers LL, puis en écrivant à la suite QQ, dont chaque saut vers jj devient un saut vers j+Lj + L. Son calcul est celui de PP, suivi, si PP s'arrête, de celui de QQ à partir de la configuration atteinte ; il s'arrête si et seulement si les deux s'arrêtent.

Macro-instructions. Dans un programme, on réserve un registre zz que rien n'incrémente et qui vaut donc toujours 00 ; alors DEC(z,j)\mathrm{DEC}(z, j) est un saut inconditionnel vers jj. Avec lui, on écrit des programmes, enchaînés par (c) :

EFF(i)\mathrm{EFF}(i), qui vide RiR_i en le décrémentant jusqu'à 00 ;

TRANS(i→j)\mathrm{TRANS}(i \to j), qui ajoute le contenu de RiR_i à RjR_j et vide RiR_i, par une boucle qui décrémente RiR_i et incrémente RjR_j ;

COPIE(i→j)\mathrm{COPIE}(i \to j), qui place dans RjR_j le contenu de RiR_i sans changer celui-ci : on vide RjR_j et un registre auxiliaire tt, on transfère RiR_i à la fois dans RjR_j et dans RtR_t, puis RtR_t dans RiR_i.

Appel. Soit QQ un programme qui calcule g:ℕk⇀ℕg : \mathbb N^k \rightharpoonup \mathbb N et ne mentionne que des registres d'indices inférieurs à mm, avec m>km > k. Pour des registres a1,…,aka_1, \dots, a_k et bb et un décalage ss, l'appel APP(Q;a1,…,ak→b)\mathrm{APP}(Q ; a_1, \dots, a_k \to b) vide les registres s,…,s+m−1s, \dots, s + m - 1, copie RalR_{a_l} dans Rs+lR_{s+l} pour l=1,…,kl = 1, \dots, k, exécute QQ renommé par i↦s+ii \mapsto s + i, puis copie RsR_s dans RbR_b.

Par (a) et (b), le programme renommé opère sur le bloc s,…,s+m−1s, \dots, s + m - 1 comme QQ sur une configuration initiale : l'appel s'arrête si et seulement si g(Ra1,…,Rak)g(R_{a_1}, \dots, R_{a_k}) est défini, et place alors cette valeur dans RbR_b. On choisit toujours ss, tt et zz au-delà de tous les registres qui portent des données, les registres tt et zz distincts entre eux et hors du bloc s,…,s+m−1s, \dots, s + m - 1, si bien qu'un appel ne modifie que RbR_b et des registres auxiliaires.

Récurrence sur les fonctions récursives. L'ensemble des fonctions partielles calculables par machine contient les fonctions de base et il est stable par les trois opérations ; il contient donc toutes les fonctions récursives.

Base. Le programme vide calcule ZZ, puisque R0R_0 vaut 00 au départ. TRANS(1→0)\mathrm{TRANS}(1 \to 0) suivi de INC(0)\mathrm{INC}(0) calcule SS, et TRANS(i→0)\mathrm{TRANS}(i \to 0) calcule PinP^n_i.

Composition. Pour h=f(g1,…,gm)h = f(g_1, \dots, g_m), on enchaîne les appels qui placent gl(R1,…,Rn)g_l(R_1, \dots, R_n) dans des registres neufs yly_l, puis l'appel qui place f(Ry1,…,Rym)f(R_{y_1}, \dots, R_{y_m}) dans R0R_0. Si l'un des gl(x)g_l(x) n'est pas défini, ou si ff ne l'est pas en (g1(x),…,gm(x))(g_1(x), \dots, g_m(x)), l'appel correspondant ne s'arrête pas.

Récurrence primitive. Pour h(x,0)≃f(x)h(x, 0) \simeq f(x) et h(x,y+1)≃g(x,y,h(x,y))h(x, y + 1) \simeq g(x, y, h(x, y)), avec yy dans Rn+1R_{n+1}, on place d'abord f(x)f(x) dans un registre neuf vv.

Puis, tant que DEC(n+1,⋅)\mathrm{DEC}(n + 1, \cdot) trouve Rn+1R_{n+1} non nul, on place g(R1,…,Rn,Rc,Rv)g(R_1, \dots, R_n, R_c, R_v) dans un registre neuf v′v', on vide RvR_v, on transfère Rv′R_{v'} dans RvR_v et l'on incrémente un compteur RcR_c, initialement nul ; enfin on transfère RvR_v dans R0R_0.

Au début du tour de rang kk, compté à partir de 00, Rc=kR_c = k et Rv=h(x,k)R_v = h(x, k), par récurrence sur kk ; la boucle fait yy tours. Sans paramètre, on place d'abord l'entier aa dans RvR_v par aa instructions INC(v)\mathrm{INC}(v).

Minimisation. Pour h(x)≃μy [f(x,y)=0]h(x) \simeq \mu y\, [f(x, y) = 0] : on place f(R1,…,Rn,Rc)f(R_1, \dots, R_n, R_c) dans un registre neuf ww ; si DEC(w,⋅)\mathrm{DEC}(w, \cdot) trouve RwR_w nul, on transfère RcR_c dans R0R_0 et l'on s'arrête ; sinon on incrémente RcR_c et l'on recommence. Le calcul s'arrête si et seulement si f(x,0),…,f(x,y)f(x, 0), \dots, f(x, y) sont définis, les premiers non nuls et le dernier nul, et il rend alors yy : c'est exactement la définition de la minimisation.

□
Remarque

La preuve est celle d'un compilateur : chaque opération de construction d'une fonction récursive se traduit en un morceau de programme, et l'appel joue le rôle d'un sous-programme, dont les registres de travail sont tenus à l'écart de ceux de l'appelant. Le seul point délicat est cette séparation, qui repose sur la localité (a).

Remarque

La réciproque, objet du nœud Kleene, demande de coder les programmes et leurs calculs par des entiers ; les deux sens ensemble montrent que les deux définitions de la calculabilité coïncident.

Ouvrir dans l'atlas interactif →