1. Terminaison et complexité

Matière à réfléchir – Compte à rebours

Voici une version naïve du compte à rebours des secondes pour le passage du Nouvel An :

Qu’arrive-t-il lorsqu’on exécute ce programme ?

Corriger le programme pour qu’il s’arrête à 0.

Qu’arrive-t-il lorsque l’on exécute la nouvelle version du programme avec la valeur -10 en entrée ou compte_a_rebours(-10) ?

1.1. Principe de terminaison

La terminaison est une propriété essentielle des algorithmes, qui garantit que les calculs de l’algorithme finiront par s’arrêter. Lorsque l’on conçoit un algorithme, il est important de faire en sorte que les calculs s’arrêtent à un moment donné. Cette garantie doit tenir pour toutes les entrées possibles. Voici un exemple d’algorithme qui compte et qui ne se termine pas :

 # Algorithme qui compte infini 
 Variable i : numérique
 i ← nombre donné par l’utilisateur
 Tant que i > 0 
	i  ← i + 1 
 	Afficher i   
 Fin Tant que

Si on exécute cet algorithme, le programme ne s’arrête jamais : i est incrémenté de 1 indéfiniment.  En pratique, si on retranscrit cet algorithme en programme et que l’on exécute le programme, le programme finira par s’arrêter lorsque les nombres représentés seront trop grands pour être représentés.

Exercice 1 – L’infini en programme

Retranscrire l’algorithme infini en programme. Après combien de boucles le programme s’arrête ?

Solution 1 – L’infini en programme

Pour faire en sorte que le programme finisse par s’arrêter, nous pouvons le modifier ainsi :

 # Algorithme qui compte toujours infini 
 Variable i : numérique
 i ← nombre donné par l’utilisateur
 Tant que i != 10000 
	i  ← i + 1 
 	Afficher i   
 Fin Tant que

Exercice 2 – L’infini ne finit plus de finir

L’algorithme ci‑dessus est appelé « Algorithme qui compte toujours infini ». Pourquoi est‑il toujours infini ? Dans quel cas cet algorithme ne s’arrête jamais ?

Solution 2 – L’infini ne finit plus de finir

Dans la version ci‑dessus, si l’utilisateur entre une valeur plus grande que 10000, ou encore une valeur à virgule, l’algorithme ne s’arrête pas. Il peut être implicite pour la personne qui programme qu’un décompte se fait toujours avec des nombres entiers, mais il doit prendre des précautions face aux utilisateurs. Voici une version de l’algorithme de décompte qui s’arrête dans tous les cas :

 # Algorithme qui compte et qui s’arrête 
 Variable i : numérique
 i ← nombre donné par l’utilisateur
 Tant que i < 10000 
	i  ← i + 1 
 	Afficher i   
 Fin Tant que

En programmant, nous devons nous assurer que nos programmes se terminent dans tous les cas, autrement il ne seront pas utilisables. Il ne suffit pas de compter sur la bienveillance des utilisateurs.

Le saviez-vous ? – Conjecture de Syracuse

De nos jours, on ne sait toujours pas si ce programme termine pour chaque entrée n. Ce problème est connu sous le nom la conjecture de Collatz ou la conjecture de Syracuse :

1.2. Principe de complexité

Matière à réfléchir – Record de vitesse

On souhaite comparer deux algorithmes qui permettent de résoudre le même problème, afin d’utiliser l’algorithme qui permet de résoudre le problème plus rapidement. Mais comment pourrait‑on calculer la vitesse d’un algorithme ?

Il est important lorsqu’on utilise un algorithme de nous préoccuper de son efficacité. Mais comment calculer l’efficacité d’un algorithme, comment calculer sa vitesse ?

Est‑ce qu’on peut utiliser la taille de l’algorithme pour prédire le temps qu’il va prendre à s’exécuter ? En d’autres termes, est‑ce qu’un algorithme de 10 lignes est toujours plus lent qu’un algorithme de 5 lignes ? Nous avons vu que l’algorithme infini du chapitre précédent est très court (seulement 5 lignes), mais en théorie il ne s’arrête jamais. Une boucle rallonge le code de seulement 2 lignes, mais rallonge le temps d’exécution de manière importante.

On pourrait croire qu’il suffit de programmer un algorithme et de chronométrer le temps que ce programme prend à s’exécuter. Cette métrique est problématique, car elle ne permet pas de comparer différents algorithmes entre eux lorsqu’ils sont exécutés sur différentes machines. Un algorithme lent implémenté sur une machine dernière génération pourrait prendre moins de temps à s’exécuter qu’un algorithme rapide implémenté sur une machine datant d’une dizaine d’années.

Pour mesurer le temps d’exécution (ou la vitesse) d’un algorithme, il existe un critère plus objectif : le nombre d’instructions élémentaires. De manière formelle et rigoureuse, on ne parle pas d’efficacité, mais plutôt de la complexité d’un algorithme, qui est en fait contraire à son efficacité. L’analyse de la complexité d’un algorithme étudie la quantité de ressources, par exemple de temps, nécessaires à son exécution.

Le saviez-vous ? – Compliqué = complexe ?

Est‑ce que complexe veut dire la même chose que compliqué ? Une chose compliquée est difficile à saisir ou à faire, alors qu’une chose complexe est composée d’éléments avec de nombreuses interactions imbriquées.

Ai-je compris ?

Vérifiez votre compréhension :

  1. Je sais que l’on doit garantir la terminaison d’un algorithme.

  2. Je sais que la complexité d’un algorithme peut nous donner une indication de sa vitesse.

  3. Je sais que la complexité est une fonction du nombre d’instructions élémentaires.