Exercice calculabilite bac
Exercice 1⚓︎
D'après le sujet de bac du 12 septembre 2024, Métropole, J2, Exercice 1 (24-NSIJ2ME3)
Cet exercice porte sur l’exécution d’un programme Python et sur la décidabilité.
Dans cet exercice, on dira qu’un appel f(x), où f est une fonction Python prenant un argument et x est une valeur, termine lorsque l’évaluation de f(x) renvoie toujours une valeur au bout d’un nombre fini d’étapes. À l'opposé, un tel appel ne termine pas s'il est
possible qu'il effectue des instructions à l'infini.
Partie A : boucle while⚓︎
Commençons par un premier exemple, avec une fonction prenant un entier
en argument et utilisant une boucle while.
1. Sur votre copie, donner les valeurs successives de la variable i
lors de l'exécution de f1(7) et indiquer si f1(7) termine.
Réponse
i = 7: initialisationi = 8, puis le testi != 10est vraii = 9, puis le testi != 10est vraii = 10, puis le testi != 10est faux- La fonction renvoie
10.
f1(7) termine donc et renvoie 10.
2. Indiquer si l'appel f1(-2) se termine. Si oui, indiquer la valeur renvoyée.
Réponse
i = -2: initialisationi = -1, puis le testi != 10est vraii = 0, puis le testi != 10est vraii = 1, puis le testi != 10est vraii = 2, puis le testi != 10est vrai- ...
i = 9, puis le testi != 10est vraii = 10, puis le testi != 10est faux- La fonction renvoie
10.
f1(-2) termine donc et renvoie 10.
3. Sur votre copie, donner les 5 premières valeurs prises par la variable i
lors de l'exécution f1(12), et indiquer si l'appel f1(12) va terminer ou non.
Réponse
i = 12: initialisationi = 13, puis le testi != 10est vraii = 14, puis le testi != 10est vraii = 15, puis le testi != 10est vraii = 16, puis le testi != 10est vrai
f1(12) ne termine donc pas car la condition i != 10 est toujours vraie. On ne sort pas de la boucle while
4. Préciser pour quels entiers n l'appel f1(n) se termine.
Réponse
- Pour
n <= 10, l'appelf1(n)se termine. - Pour
n > 10, l'appelf1(n)ne se termine pas.
Partie B : fonction récursive⚓︎
Prenons maintenant un deuxième exemple, avec une fonction récursive (prenant, elle aussi, un entier en argument).
5. L'appel f2(4) termine-t-il ?
Si oui, indiquer la valeur renvoyée par f2(4) ; sinon, justifier brièvement.
Réponse
f2(4)essaie de calculer4 + f2(2), donc il y a un appel àf2(2).f2(2)essaie de calculer2 + f2(0), donc il y a un appel àf2(0).f2(0)renvoie immédiatement 0.f2(2)renvoie alors2 + 0, donc2.f2(4)renvoie alors4 + 2, donc6.
f2(4) termine et renvoie 6.
6. L'appel f2(5) termine-t-il ?
Si oui, indiquer la valeur renvoyée par f2(5) ; sinon, justifier brièvement.
Réponse
f2(5)essaie de calculer5 + f2(3), donc il y a un appel àf2(3).f2(3)essaie de calculer3 + f2(1), donc il y a un appel àf2(1).f2(1)essaie de calculer1 + f2(-1), donc il y a un appel àf2(-1).f2(-1)essaie de calculer-1 + f2(-3), donc il y a un appel àf2(-3). La condition d'arretif n == 0:n'est jamais atteinte carnne prend que des valeurs impaires.
f2(5)ne termine pas donc pas.
Remarque
Si on exécute ce code, au bout de 1000 appels récursifs (valeur par défaut) un mécanisme de Python stoppe le programme.
Il s'affiche alors : RecursionError: maximum recursion depth exceeded
7. Déterminer l'ensemble des entiers naturels n pour lesquels l'appel f2(n) termine.
Réponse
L'appel f2(n) termine pour tous les entiers pairs et positifs.
8. Écrire une fonction Python infini, récursive, telle que
l'appel infini(n) ne termine pour aucun entier n.
Réponse
def infini(n):
"""n est un entier naturel donc positif ou nul."""
if n == -1: # Cette condition ne sera jamais réalisée
return 0
else:
return infini(n + 1)
Une autre réponse possible :
Partie C : le problème de l'arrêt⚓︎
On se demande maintenant s'il est possible d'écrire une fonction arret qui
prend en arguments une chaine de caractères code_f contenant le code
d'une fonction f et un argument x de f, et tel que
arret(code_f, x) renvoie True si l'appel f(x) va terminer et False sinon.
Dans la suite de cet exercice, on suppose disposer d'une fonction arret
et on implémente la fonction suivante, utilisant cette fonction arret,
ainsi que la fonction infini de la question précédente
dont l'appel infini(n) ne termine jamais, quelle que soit la valeur de n.
De même, on suppose disposer d'une variable code_paradoxe contenant
le code de la fonction paradoxe sous la forme d'une chaine de caractères,
et on s'intéresse à l'appel paradoxe(code_paradoxe).
Cet appel de fonction commence par effectuer le test
arret(code_paradoxe, code_paradoxe) dans le if de la ligne 2.
9. Dans le cas où arret(code_paradoxe, code_paradoxe) renvoie True,
préciser la prochaine instruction à être exécutée.
Dans ce cas, expliquer si l'appel paradoxe(code_paradoxe) termine.
Réponse
Si arret(code_paradoxe, code_paradoxe) renvoie True, alors la prochaine instruction sera infini(42) qui ne termine pas. Cela prouve que l'appel paradoxe(code_paradoxe) ne termine pas.
10. Dans le cas où arret(code_paradoxe, code_paradoxe) renvoie False,
préciser la prochaine instruction à être exécutée.
Dans ce cas, expliquer si l'appel paradoxe(code_paradoxe) termine.
Réponse
Si arret(code_paradoxe, code_paradoxe) renvoie False, alors la prochaine instruction sera return 0 qui termine. Cela prouve que l'appel paradoxe(code_paradoxe) termine.
11. En déduire qu'une telle fonction arret ne peut exister.
Réponse
Supposons que la fonction arret existe.
arret(code_paradoxe, code_paradoxe) peut renvoyer soit True, soit False.
- Supposons que
arret(code_paradoxe, code_paradoxe)renvoieTrue. D'après la question 9. l'appelparadoxe(code_paradoxe)ne termine pas. Donc ce n'est pas possible car il faudrait quearret(code_paradoxe, code_paradoxe)renvoieFalse, ce qui est en contradiction avec notre hypothèse. - Supposons que
arret(code_paradoxe, code_paradoxe)renvoieFalse. D'après la question 10. l'appelparadoxe(code_paradoxe)termine. Donc ce n'est pas possible car il faudrait quearret(code_paradoxe, code_paradoxe)renvoieTrue, ce qui est en contradiction avec notre hypothèse.
On a démontré que si la fonction arret existait, ce n'était pas possible. Ceci montre "par l'absurde" que la fonction arret ne peut pas exister.
Crédits⚓︎
La plus grande partie de cet exercice est retranscrite et corrigée par Franck Chambon sous licence cc-by-nc-sa.
Modifications : Mireille Coilhac.
Exercice 2⚓︎
Sujet de bac Asie jour 1 session 2025
Décidabilité, algorithmique et programmation Python
En Python, on peut utiliser le triple guillemet pour écrire une chaîne de caractères sur plusieurs lignes. Par exemple, on peut définir une variable programme1 qui contient la chaîne de caractères correspondant à un petit programme Python de la manière suivante :
De même, on peut définir la variable programme2 :
1. On suppose que l'on exécute le programme contenu dans la variable programme1. Donner les valeurs de x et de y après exécution de ce programme.
Réponse
x vaut 0 et y vaut 20.
2. Expliquer pourquoi tout programme Python peut être vu comme une chaîne de caractères.
Réponse
Un fichier Python est un fichier texte, on peut donc le percevoir comme une chaine de caractères.
En Python, la fonction exec permet d'exécuter le programme correspondant à une chaîne de caractères passée en paramètre.
On considère les quatre variables programme3, programme4, programme5 et programme6 suivantes :
programme3 = """
x = 10
while x != 0:
x = x - 2
"""
programme4 = """
x = 10
while x > 0:
x = x + 2
"""
programme5 = """
x = 10
while x < 0:
x = x + 4
"""
programme6 = """
x = 10
while x != 0:
x = x - 4
"""
3. On exécute les variables programme3, programme4, programme5 et programme6 avec la fonction exec. Déterminer lesquelles terminent et lesquelles ne terminent pas.
Réponse
Les programmes 3 et 5 terminent.
Les programmes 4 et 6 ne terminent pas.
On cherche à écrire une fonction arret telle que arret(programme) renvoie True si exec(programme) termine et False si exec(programme) ne termine pas. Cette fonction arret doit donc s'arrêter dans tous les cas.
4. Indiquer ce que réalise le programme suivant et s'il permet de répondre au problème posé ci-dessus.
Réponse
Ce programme ne répond pas au problème car si le programme ne s'arrête pas (à la ligne 2), le programme ne renverra jamais False.
On suppose disposer d'une fonction recherche(mot, texte) qui renvoie True si une chaîne de caractères mot est présente dans une chaîne de caractères texte et False sinon.
5. Expliquer succinctement le principe de l'algorithme de Boyer-Moore qui permet d'implémenter cette fonction recherche.
Réponse
L'algorithme de Boyer-Moore est un algorithme de recherche de texte qui utilise un dictionnaire pour faire des décalages intelligents et ainsi accélérer la recherche.
Une idée est d'écrire une fonction qui décrète qu'un programme s'arrête s'il ne contient pas de while et ne s'arrête pas s'il en contient un.
6. Écrire une fonction arret_essai2(programme) qui renvoie True si la chaîne de caractères "while" n'est pas utilisée dans la chaîne de caractères programme et False sinon.
7. Montrer qu'il est possible que :
arret_essai2(programme)renvoieTruealors que le programme ne s'arrête pas ;arret_essai2(programme)renvoieFalsealors que le programme s'arrête.
Indication : il n'y a pas que les boucles
whilequi peuvent poser des problèmes de non terminaison.
Réponse
Avec le programme suivant :
arret_essai2(programme) renverra True alors que le programme ne s'arrête pas.
Avec le programme suivant :
arret_essai2(programme) renverra False alors que le programme s'arrête.
Nos tentatives pour écrire une telle fonction arret sont restées vaines. Nous allons montrer qu'il est en réalité impossible d'écrire une telle fonction. On va supposer qu'une telle fonction arret existe. On va montrer que cette supposition aboutit à un paradoxe ce qui prouvera que la supposition est fausse.
8. Écrire une fonction terminaison_inverse telle que l'appel terminaison_inverse(programme) termine si la chaîne de caractères programme représente un programme qui ne termine pas et ne termine pas si la chaîne de caractères programme représente un programme qui termine. On pourra utiliser la fonction boucle_infinie de programme2 ainsi bien sûr que la fonction arret dont on a supposé l'existence.
Réponse
On considère "terminaison_inverse(programme_paradoxal)" qui n'est rien d'autre qu'une chaîne de caractères, et on définit une variable que l'on appelle programme_paradoxal à laquelle on affecte cette chaîne de caractères :
9. Étudier si le programme paradoxal termine ou non, c'est-à-dire si exec(programme_paradoxal) termine ou non.
Réponse
Si programme_paradoxal termine, alors terminaison_inverse(programme_paradoxal) ne termine pas.
Si programme_paradoxal ne termine pas, alors terminaison_inverse(programme_paradoxal) termine.
10. Indiquer ce que l'on peut conclure sur la fonction arret.
Réponse
La question 9. a abouti à une contradiction. Celle-ci est due à la non-existence de la fonction arret.
11. Expliquer si l'impossibilité d'écrire une telle fonction arret est due aux limitations du langage Python.
Réponse
Cette impossibilité n'est pas due aux limitations du langage Python. Elle est générale, et a été démontrée en 1936 par Alan Turing sous le nom Théorème de l'arrêt.
Crédits⚓︎
Gilles Lassus