Files
TermNSI/Calculabilité/README.md

15 KiB
Raw Blame History

Calculabilité et décidabilité en informatique

À chaque problème sa non solution !

Le BO

bo

Logique

Introduction à la décidabilité

Il vous est déjà arrivé de prendre des décisions en suivant une logique. Dans la vie vous suivez la votre, en prenant des conseils, ou de par ce que vous avez appris à l'école. C'est donc quelque chose général.

Cela vous semble simple de définir la logique, mais en réalité, mise en pratique, cela devient plus complexe à commenter : mathématiquement, la logique n'a pas grand chose à voir avec celle dont on use au quotidien.

En informatique, une chose primordiale est l'utilisation et la mise au point d'algorithmes, et donc de prise de décision. Ces décisions sont prises par le programme informatique via un schéma de pensée que l'on peut représenter comme ceci :

Diagramme

D'un point de vue mathématique ou informatique, ces paramètres et prises de décision sont de type booléen (vrai ou faux).

On peut par exemple prendre des problèmes assez élémentaires :

- X est il un nombre pair ?
- Y est il une puissance de 3 ?
- Dans un graphe, existe-t-il un chemin passant par tous les sommets de longueur inférieure à k ?

Dans notre premier cas, nous avons en instance de départ : un entier naturel, qui, après être traité via le paramètre choisi (Si reste = 0 après une division par 2, alors nombre est pair) dans l'algorithme, ressortira en une réponse simple : Oui ou Non selon la décision prise.

Définition : Prédicat

En informatique ou mathématiques, on peut parler d'une fonction algorithmique. Quand le résultat de celle-ci, c'est-à-dire ce qu'elle renvoie, est un booléen, on parle de prédicat.

Un prédicat est une fonction qui ne prendra que des valeurs booléennes.

La réponse à un problème de décision est donc un booléen : oui ou non.

Exemple : « Ce chemin passe-t-il par tous les sommets du graphe ? » est une question dont la réponse est Vrai ou Faux : la fonction qui y répond est bel et bien un prédicat.

Définition : Décidabilité

Si, pour réponse à un problème, on peut en écrire un algorithme permettant la prise de décision et donc amenant à la résolution du dit problème, on parlera de problème décidable ou de décidabilité

Et donc, par extension, si un problème n'est pas soluble, on utilisera le terme Indécidabilité


La calculabilité

En informatique, plus précisément dans la branche dite de programmation, nous utilisons un langage qui nous est propre afin de mettre en place nos idées dans un algorithme.

Vocabulaire

Cela ne signifie pas pour autant que cet algorithme fonctionne : pour cela il faut vérifier que la propriété qu'on appelle Terminaison est bien respectée, et que l'algorithme nous mène bien à la réponse souhaitée.

On parlera également d'algorithme de haut niveau: l'algorithme doit pouvoir être traduit en n'importe quel langage de programmation, il ne doit donc pas faire appel à des notions techniques relatives à un programme particulier ou bien à un système d'exploitation donné.

Quel rapport avec la calculabilité ? Et bien une fonction mathématique, que l'on peut retranscrire sous forme d'algorithme afin de la programmer dans un langage informatique utilisé, est dite Calculable

Un peu d'histoire

En 1936, un mathématicien américain, du nom d'Alonzo Church, formule ce qu'on appellera la "thèse de Church" (la même année, Alan Turing arrive indépendamment à un résultat équivalent avec sa machine). Cette thèse affirme la notion de calculabilité : Tout traitement d'un système réalisable par un processus quel qu'il soit, peut être exprimé par un ensemble de règles de calcul.

Cet ensemble est défini lui même par les règles de calcul dont on prouve mathématiquement l'équivalence.

Cette thèse propose les règles suivante en matière d'algorithme :

1. L'algorithme consiste en un ensemble fini d'instructions simples et précises 
qui sont décrites avec un nombre limité de symboles.
2. L'algorithme doit toujours produire le résultat en un nombre fini d'étapes.
3. L'algorithme peut être suivi par un humain avec seulement du papier et un crayon.
4. L'exécution de l'algorithme ne requiert pas d'intelligence de l'humain 
        sauf celle qui est nécessaire pour comprendre et exécuter les instructions.

Trop long, j'ai pas lu !

Pour résumer : une fonction mathématique qui fonctionne selon un modèle existant fonctionnera dans n'importe quel modèle.

Si vous décidez de programmer une fonction qui vous dit si un nombre est pair en python, votre fonction devrait fonctionner dans un autre langage, pourvu que vous utilisiez les "codes" de ce langage.

Ici, en NSI, nous utilisons le langage Python, mais nous pourrions parfaitement transposer nos codes en C++, Java ou autre.

Et la calculabilité dans tout ça ?

Revenons au prédicat : si une fonction renvoie un prédicat, et que celui là est dit calculable, alors la prise de décision est possible. Une fonction calculable permet donc l'emergence de décisions décidables.

Ce qui signifie qu'il existe un algorithme permettant la résolution de cette fonction, ou d'un problème amené par la fonction, et si un algorithme existe, alors nous sommes en mesure de programmer la fonction dans n'importe quel langage.


La décidabilité

Vocabulaire

Une propriété est dite décidable si l'on peut déterminer son résultat (vrai ou faux) en un nombre fini d'étapes

C'est le principe même d'un algorithme, ou d'une recette de cuisine par exemple : on indique un nombre d'étapes qui doit mener, à terme, à un résultat.

Pour un problème, c'est la même chose : si l'on peut écrire un programme qui renvoit un prédicat permettant de calculer la réponse au problème de départ, alors on parlera de problème décidable.

Pourquoi est ce important ?

Imaginez, si votre programme, logiciel ou jeu vidéo favori rencontrait un problème d'execution et tournait en boucle quand vous l'utilisez.

Frustrant n'est ce pas ? Une personne qui souhaite développer des programmes doit s'assurer de la décidabilité de son travail.

Et maintenant, des exemples !

Exercice : Prouver qu'un nombre est pair :

Nous allons définir une fonction f : \mathbb{N} \rightarrow \{\text{Vrai}, \text{Faux}\} telle que f(n) est vraie si et seulement si n \bmod 2 = 0.

Nous savons que ce problème est résoluble et donc décidable

Son algorithme est très simple : il suffit de diviser par 2 et d'observer le reste de la division : si celui ci est égal à 0, alors il s'agit d'un nombre pair. Sinon, ce nombre est impair.

On peut maintenant écrire notre algorithme sous forme de langage python :

def est_pair(n):
    ''' Renvoie un prédicat exprimant le résultat de n modulo 2
    Si le reste est nul, alors le prédicat est vrai et le nombre n est pair, sinon, il est impair.
    
    '''
    return n % 2 == 0
>>>est_pair(42)
>>>True
>>>est_pair(113)
>>>False

Si ce code fonctionne, il ne faut pas oublier une chose : ça n'est pas la seule solution existante au problème : puisque ce problème est décidable, vous pouvez écrire votre propre algorithme qui fera tout aussi bien !

Par exemple :

def est_pair2(n):
    reste = n % 2
    if reste == 0:
        return True
    else:
        return False
>>>est_pair2(113)
>>>False
>>>est_pair2(42)
>>>True

Remarque : cette version renvoie bien un booléen, comme la première. Une fonction qui renverrait une chaîne de caractères comme "42 est Pair" ne serait pas un prédicat !

Nous avons bien la preuve que le problème " Peut-on prouver qu'un nombre est pair" est bel et bien décidable.

Tous les problèmes sont ils décidables?

Et bien non ! Disons le tout de suite, il existe un nombre de problèmes que l'on ne peut résoudre par un simple algorithme. On parlera de Problèmes Indécidables !

La question qui se pose à nous est de savoir comment reconnaitre un problème indécidable.

Et à l'heure actuelle, nous ne disposons pas de machine magique permettant de nous donner cette information.

Pour prouver qu'un problème est décidable, il suffit d'exhiber un algorithme qui le résout et qui se termine toujours. Mais pour prouver qu'un problème est indécidable, c'est bien plus difficile : il faut démontrer qu'aucun algorithme, même parmi ceux qu'on n'a pas encore inventés, ne pourra jamais le résoudre.

En informatique, le plus célèbre de ces problèmes indécidables est le problème de l'arrêt.

Le problème de l'arrêt

En 1936, le mathématicien britannique Alan Turing démontre qu'il est impossible d'écrire un algorithme qui, pour n'importe quel programme et n'importe quelle entrée, dirait avec certitude si ce programme s'arrête ou tourne à l'infini.

Sa démonstration repose sur un raisonnement par l'absurde : on suppose qu'un tel algorithme existe, et on montre que cette supposition mène inévitablement à une contradiction.

Le programme qui détermine l'arrêt est lui-même indécidable

On peut même dire absurde - et c'est justement le mot qu'on va utiliser.

En raisonnement par l'absurde, on suppose vraie la chose qu'on veut réfuter, puis on montre que cette hypothèse aboutit à une contradiction logique.

Vous suivez toujours ?

Supposons que l'on puisse écrire une fonction arret, capable de dire si une fonction programme va s'arrêter dans tous les cas ou s'il existe des cas où elle ne s'arrête pas.

Il n'est, dans notre exemple, absolument pas certain qu'une telle fonction existe.

Nous allons dans un premier temps, créer une fonction qui nous servira de cobaye dans notre fonction absurde de programme d'arrêt.

def arret_programme(programme, x):
    '''retourne vrai si le programme s'arrête ou non selon le paramètre x
    '''
    predicat = ...
    if predicat:
        return True
    else:
        return False
    
    

Dans le code ci dessus, si notre condition est vraie, alors le programme renverra True. Sinon, il renverra False.

Jusqu'ici, tout est logique, et c'est comme cela que la plupart des programmes fonctionne.

Maintenant, définissons notre fonction absurde

def absurde(programme):
    if arret_programme(programme, programme):
        # Si le programme s'arrête, on boucle à l'infini
        while True:
            pass
    else:
        # Si le programme boucle, on s'arrête
        return True

Analysons ce que l'on vient d'écrire :

  • Si arret_programme(programme, programme) renvoie True (le programme s'arrête), alors absurde boucle à l'infini
  • Si arret_programme(programme, programme) renvoie False (le programme boucle), alors absurde s'arrête et renvoie True

Le paradoxe : Que se passe-t-il si on appelle absurde(absurde) ?

  • Si arret_programme(absurde, absurde) dit que absurde(absurde) s'arrête → alors absurde(absurde) boucle (contradiction !)
  • Si arret_programme(absurde, absurde) dit que absurde(absurde) boucle → alors absurde(absurde) s'arrête (contradiction !)

Nous venons donc de prouver l'impossible. Pas mal non ?

Conclusion

Turing prouve donc qu'il n'est pas possible d'écrire une fonction capable de dire, dans tous les cas, si un programme quelconque s'arrête ou non.

Si une telle fonction existait, on pourrait l'utiliser pour résoudre absurde(absurde) - et on vient de montrer que c'est impossible. Le problème de l'arrêt est donc indécidable : aucun algorithme ne peut le résoudre.

Si la machine ne peut pas nous aider ici, il vous faudra toujours, en tant que programmeur, vérifier vous-même que votre code se termine.


À retenir

Gödel (1931) - Incomplétude des systèmes formels

Dans tout système logique suffisamment puissant pour décrire l'arithmétique, il existe des énoncés vrais mais indémontrables dans ce système. Autrement dit, les mathématiques ont des limites intrinsèques : certaines vérités échappent à toute preuve formelle.

Turing (1936) - Indécidabilité du problème de l'arrêt

Il n'existe aucun algorithme capable de déterminer, pour tout programme et toute entrée, si ce programme s'arrête ou tourne indéfiniment. Le problème de l'arrêt est indécidable.

Le lien entre les deux

Ces deux résultats sont distincts mais convergent vers la même idée : il existe des limites fondamentales à ce que la logique formelle et le calcul algorithmique peuvent résoudre. Certains problèmes sont hors de portée, non pas par manque de puissance de calcul, mais par nature.

Turing (programme) Gödel (pour aller plus loin)
Domaine Informatique / algorithmique Mathématiques / logique formelle
Résultat Il existe des problèmes insolubles par algorithme Il existe des vérités indémontrables dans tout système formel
Exemple clé Problème de l'arrêt Théorème d'incomplétude (1931)

Pour aller plus loin : Kurt Gödel et l'incomplétude

⚠️ Cette section est hors programme NSI.

En 1931, le mathématicien autrichien Kurt Gödel énonce son théorème d'incomplétude, qui bouleverse les mathématiques.

Ce qu'il prouve

Dans tout système logique suffisamment puissant pour décrire l'arithmétique (l'addition, la multiplication sur les entiers), il existe des énoncés qui sont vrais mais indémontrables dans ce système.

Autrement dit : les mathématiques ne peuvent pas tout prouver. Certaines vérités leur échappent structurellement.

La notion d'axiome

Gödel travaille sur la notion d'axiome : une proposition qu'on accepte comme vraie sans la démontrer, et qui sert de point de départ à un raisonnement.

Par exemple, en géométrie euclidienne : "par deux points distincts, il passe une droite et une seule" est un axiome. On ne le prouve pas - on le pose.

Gödel montre que quel que soit l'ensemble d'axiomes choisi, il restera toujours des énoncés que ce système ne peut ni prouver ni réfuter.

Le lien avec Turing

Gödel (1931) et Turing (1936) arrivent indépendamment à des conclusions similaires par des chemins différents :

  • Gödel montre les limites de la démonstration mathématique
  • Turing montre les limites du calcul algorithmique

Les deux résultats disent, chacun à leur manière, qu'il existe des questions auxquelles ni les mathématiques ni les ordinateurs ne peuvent répondre - non par manque de puissance, mais par nature.


Auteur : Florian Mathieu

Licence CC BY NC

Licence Creative Commons
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas dUtilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.