Algorithme de dichotomie
Ce deuxième projet d'approximation numérique va te faire découvrir un algorithme beaucoup plus rapide que le balayage.
Souviens toi du jeu que tu as créé dans le tout premier projet, le juste prix. Quelle était la meilleure stratégie, c'est-à-dire la stratégie qui gagne en général le plus rapidement ?
Le mieux c'est de choisir le centre de l'intervalle. On doit tout d'abord choisir un nombre entre 0 et 100 donc on prend le centre qui est 50. Si ce nombre est trop petit, le nouvel intervalle de recherche est [50; 100]. On choisi ensuite le centre de cet intervalle, ici 75. On continue comme ça jusqu'à trouver le nombre.
Voici un exemple d'une partie utilisant cette stratégie :
Nombre choisi par le joueur | Réponse ordinateur | Nouvel interval |
---|---|---|
Trop petit | ||
Trop grand | ||
Trop petit | ||
C'est ça ! |
- 1
J'ai redéfini la fonction
jeu
du juste prix. Appelle-la dans la console et fais une partie en appliquant l'algorithme de dichotomie.Solution
On appelle la fonction
jeu
et on suit la procédure décrite dans l'introduction : prendre le milieu de chaque nouvel intervalle de recherche>>> jeu()
- 2
Je viens de définir la fonction
f
dans ton éditeur. Cette fonction renvoie le carré d'un nombre, et comme dans le projet précédent, nous allons déterminer une approximation de . Par dichotomie cette fois !L'algorithme repose sur la présence d'un intervalle dont on calcule le milieu. Comme on souhaite calculer une approximation de on choisit un intervalle de départ qui contient ce nombre. Par exemple on peut prendre l'intervalle [1; 2]
En-dessous de la fonctionf
défini une variablea
initialisée à 1 et une variableb
initialisée à 2. Ces variables représentent les bornes de l'intervalle de départ dans lequel on recherche l'approximation.Solution
On initialise
a
à 1 etb
à 2def f(x): return x**2 a = 1 b = 2
- 3
L'algorithme consiste à répéter successivement une même procédure : déterminer l'intervalle de recherche puis prendre son milieu. Il faut donc qu'on utilise une boucle. Comme on ne sait pas à l'avance combien d'étapes sont nécessaires, on utilise une boucle while.
Pour le moment écrivons uniquement le code correspondant à la première itération pour bien comprendre. On ajoutera la boucle while à la prochaine question :
- On commence par calculer le milieu de l'intervalle [a; b].
Crée une variablem
et utilise la formule du milieu pour lui assigner comme valeur le centre de l'intervalle [a; b]. - Ensuite il faut déterminer si
m
est trop petit ou trop grand et en déduire les nouvelles bornes de l'intervalle de recherche, c'est-à-dire les nouvelles valeurs dea
etb
.Si
f(m) < 2
alorsm
est trop petit puisque son carré (le nombref(m)
) est inférieur à 2. Dans ce cas, l'intervalle de recherche peut être réduit à[m; b]
. Donc il faut réassigner la variablea
pour qu'elle prenne la même valeur quem
en faisanta = m
Ajoute une instruction conditionnelle qui détermine sim
est trop petit, puis réassigne les bornes de l'intervalle de recherche en conséquence. De même sim
est trop grand (il faut adapter).
Solution
On commence par créer la variable
m
. C'est le milieu de l'intervalle [a;b] doncm
vaut 1.5 mais l'idée est d'avoir une formule, car dans la question suivante, la valeur dem
sera calculée automatiquement à chaque itération.Donc on utilise la formule du milieu
m = (a+b)/2
Ensuite l'instruction conditionnelle nous indique si
m
est trop petit ou trop grand, ce qui permet de déterminer le nouvel intervalle de rechercheOn utilise la comparaison
f(m)<2
pour savoir sim
est trop petit. Dans ce cas le nouvel intervalle de recherche est [m ; b]. Donc on réassignea
en faisanta = m
Sinon,
m
est trop grand. Alors le nouvel intervalle de recherche est [a;m] et il faut réassignerb
en faisantb = m
def f(x): return x**2 a = 1 b = 2 m = (a+b)/2 if f(m) < 2: a = m else: b = m
- On commence par calculer le milieu de l'intervalle [a; b].
- 4
Nous allons répéter la procédure de la question précédente à chaque itération de l'algorithme. Ainsi, à chaque itération on obtient une nouvelle valeur de
m
un peu plus proche du nombre cherché. À la fin du processus, la dernière valeur dem
obtenue est notre approximation.Nous allons créer une fonction dont le paramètre correspond à la précision de l'approximation recherchée. Ce paramètre va servir pour la condition d'arrêt : tant que l'écart entre
a
etb
est supérieur à la précision voulue, c'est que l'intervalle de recherche n'est pas assez fin. Donc on fait une itération supplémentaire.
Défini une fonctiondichotomie
qui dépend d'un argument, le pas. Le corps de cette fonction reprend le code des questions précédentes que tu peux indenter : initialisation dea
etb
puis instruction conditionnelle. Enfin indente ton instruction conditionnelle et le calcul dem
dans le corps d'une boucle while : tant que l'écart entrea
etb
est supérieur aupas
(condition d'arrêt), alors calcule le milieum
puis réassigne les bornes de l'intervalle. N'oublie pas de renvoyerm
à la fin.Solution
On commence par définir la fonction
dichotomie
qui dépend d'un argument, lepas
.On indente le code des questions précédentes dans le corps de cette fonction : l'initialisation de
a
, deb
etc.Ensuite on crée une boucle while. Celle-ci s'exécute tant que l'écart entre
a
etb
est supérieur aupas
, donc tant que l'intervalle de recherche est plus large que la précision voulue. Ainsi la condition d'arrêt estb-a > pas
Le corps de la boucle while reprend les étapes de la question précédente : calculer le milieu
m
de l'intervalle puis déterminer sim
est trop petit ou trop grand avec une instruction conditionnelle. On reprend le code de la question précédente.À la fin de la fonction on renvoie
m
def f(x): return x**2 def dichotomie(pas): a = 1 b = 2 while b-a > pas: m = (a+b)/2 if f(m) < 2: a = m else: b = m return m
- 5
Appelle la fonction
dichotomie
dans la console avec différentes valeurs du pas (10**-1
,10**-2
etc.) pour obtenir des approximations de plus en plus précises deSolution
Lorsque le pas diminue les approximations sont de plus en plus précises.
>>> dichotomie(10**-1) 1.4375 >>> dichotomie(10**-2) 1.4140625 >>> dichotomie(10**-3) 1.4150390625
- 6
Pour comparer la vitesse de calcul de la fonction
dichotomie
avec la fonctionbalayage
du précédent projet, nous allons ajouter un compteur.
Dans le corps de la fonctiondichotomie
, initialise une variablecompteur
avant la boucle while avec une valeur de départ de 0. À chaque itération de la boucle while, incrémente cecompteur
de 1. Enfin renvoie la valeur du compteur avec l'approximation. Rappel : tu peux renvoyer plusieurs valeurs en les séparant par une virgule. Renvoie le compteur en deuxième.Solution
On initialise la variable
compteur
à 0 avant la boucle while. Ensuite on incrémente ce compteur de 1 à chaque itération de la boucle. Enfin on renvoie le compteur avec l'approximationm
def f(x): return x**2 def dichotomie(pas): a = 1 b = 2 compteur = 0 while b-a > pas: compteur = compteur + 1 m = (a+b)/2 if f(m) < 2: a = m else: b = m return m, compteur
- 7
Dans le projet précédent, la fonction
balayage
avait besoin de 414214 calculs pour trouver une approximation de à près.>>> balayage(10**-6) 1.414213999965924,414214
Qu'en est-il de la fonctiondichotomie
? Appelle-la dans la console pour voir !Solution
La fonction
dichotomie
a besoin de beaucoup moins de calculs que la fonctionbalayage
: seulement 20 itérations pour une approximation à , 34 itérations pour une approximations à et 50 itérations pour un pas de !>>> dichotomie(10**-6) 1.4142141342163086,20 >>> dichotomie(10**-10) 1.4142135623260401,34 >>> dichotomie(10**-15) 1.414213562373095,50
Cette stratégie est la plus rapide car à chaque étape l'intervalle de recherche est deux fois plus petit qu'à l'étape précédente. On passe ainsi de l'intervalle [0; 100] à l'intervalle [50; 100] etc.
La procédure à suivre consiste à calculer les bornes de l'intervalle de recherche (initialement 0 et 100), puis à déterminer le centre de cet intervalle, et répéter ces étapes si besoin. Cette procédure porte un nom : c'est l'algorithme de dichotomie
Bonus : comme dans le précédent projet tu peux adapter ton code pour déterminer d'autres approximations.
Par exemple tu peux modifier le code de la fonction f
pour calculer des approximations numériques de . Vérifie quand même
s'il ne faut pas ajuster l'initialisation des variables a
et b
sinon
tu risques une boucle infinie.
En terme de complexité, l'algorithme de dichotomie fait partie de la famille des algorithmes les plus rapides possibles. C'est un incontournable !