Cet article peut être librement diffusé et son contenu réutilisé pour une utilisation non commerciale (contacter l’auteur pour une utilisation commerciale) suivant la licence CC-by-nc-sa
Dans le programme de mathématiques de seconde on trouve parmi la liste des compétences mathématiques travaillées :
- Calculer, appliquer des techniques et mettre en œuvre des algorithmes ;
Il y a dans ce programme des indications au sujet de ces algorithmes que l’on peut mettre en œuvre, nous allons ici les illustrer.
Nous faisons le choix de vous proposer une approche 100% fonctionnelle, comme préconisé dans les programmes.
Il peut être utile de connaître et d’utiliser Sofuspy qui propose une programmation par blocs et un traducteur vers Python, ce qui permet de faciliter l’apprentissage de Python pour des élèves formés à la programmation par blocs au collège.
Nombres et calculs
Déterminer par balayage un encadrement de √2 d’amplitude inférieure ou égale à 10−n.
Cette méthode, classique, consiste, à partir d’un intervalle [a ; b] dans lequel on sait trouver $\sqrt{2}$, à calculer x², pour x allant de a à b avec un pas de h, « Tant Que » x² < 2 est vérifiée (On utilisera pour cela un While.). La fonction carré étant croissante, dès que x² > 2 on arrête l’algorithme et on obtient un encadrement de longueur h de $\sqrt{2}$ : $x – h ≤ \sqrt{2} < x$.
Il faut déterminer a et b, les bornes de l’intervalle dans lequel on va appliquer le balayage. On prendra a = 1 et b = 2 en effet 1<2<4 donc $1 < \sqrt{2} < 2$.
Fonction qui calcule cet encadrement, renvoyé dans un tuple, en fonction du nombre n de décimales souhaitées :
Cet algorithme détermine par balayage un encadrement de racine de 2 d’amplitude 10^(-n).
- def balayage(n):
- h=10**(-n)
- x=1 #1 est la valeur de a. b ne sert à rien ici, inutile de l'introduire.
- while x**2<2:
- x=x+h
- return (round(x-h,n),round(x,n))
Utilisation : Compiler le fichier puis dans la console taper balayage(9) puis « Entrer » :
- >>> balayage(9)
- (1.414213562, 1.414213563)
Le round permet ici de corriger quelques erreurs d’affichage des flottants [1].
Il est à noter que cet algorithme est très long à donner une réponse si n>=9.
On peut donc chercher à l’optimiser, par exemple en utilisant une boucle for qui va faire la même chose que le programme précédent mais pour h allant de 10-1 à 10-n en réduisant l’intervalle [a ; b] à chaque boucle.
C’est beaucoup plus rapide, jusqu’à n=16 où ça bogue, parce que les flottants (décimaux) ne sont affichés qu’avec 16 chiffres après la virgule.
- def balayage(n):
- a=1 #1 est la valeur de a lors de la première boucle. b ne sert à rien ici, inutile de l'introduire.
- for i in range(1,n+1):
- h=10**(-i)
- x=a
- while x**2<2:
- x=x+h
- a=x-h
- return (round(x-h,n),round(x,n))
Les deux programmes précédents dans Sofuspy :
A l’ouverture de la page, vous pouvez exécuter le programme en blocs en cliquant sur « Exécuter » au-dessus des blocs, entrez une valeur de n<=6 parce qu’ils ne fonctionnent pas au-delà de n=6 car le nombre d’itérations est limité à 1000000. Cette limitation peut être levée en demandant à SofusPy la traduction automatique en Python (en cliquant sur "Traduire"), puis en exécutant le programme ainsi obtenu avec l’interpréteur Python de SofusPy (En cliquant sur "Exécuter" au-dessus du code Python).
balayage1
balayage2
Déterminer si un entier naturel a est multiple d’un entier naturel b.
On ne va bien sûr pas utiliser les opérations de base Python :
– % reste de division entière (17 % 3 donne 2)
– // quotient de division entière (17 // 3 donne 5)
il n’y aurait alors aucun intérêt à parler de cet algorithme.
On va bien sûr utiliser le théorème de la division euclidienne :
Il existe un unique couple $(q, r)\in \mathbb{N}\times\mathbb{N}$ tel que : $a = bq + r, 0 \leq r < b $.
On peut donc programmer le calcul de tous les multiples de $b$ inférieurs à $a$ et voir si l’on atteint $a$.
Nous faisons le choix de renvoyer en résultat un booléen pour indiquer si la réponse à la question que l’on pose à la fonction est vraie ou fausse.
Cet algorithme détermine si un entier naturel $a$ est multiple d’un entier naturel $b$.
$n$ sert à compter le nombre de $b$ dans $m$.
- def a_multiple_de_b(a,b):
- n=0
- m=0
- while m<=a:
- m+=b
- n+=1
- if a==(n-1)*b:
- return True
- else:
- return False
Utilisation : Compiler le programme et taper dans la console
- >>> a_multiple_de_b(569,19)
- False
- >>> a_multiple_de_b(570,19)
- True
Pour des entiers a et b donnés, déterminer le plus grand multiple de b inférieur ou égal à a.
On n’utilisera pas non plus ici les opérations de base Python % et //.
On peut donc programmer le calcul de tous les multiples de $b$ inférieurs à $a$ comme dans l’algorithme précédent, avec des modifications mineures.
Avec Python :
La fonction combien_b_dans_a détermine le plus grand multiple de $b$ inférieur ou égal à $a$.
$n$ sert à compter le nombre de $b$ dans $m$.
- def combien_b_dans_a(a,b):
- n=0
- m=0
- while m<=a:
- m+=b
- n+=1
- return n-1
Utilisation : Compiler le programme et taper dans la console
- >>> combien_b_dans_a(58,7)
- 8
- >>> combien_b_dans_a(57,7)
- 8
- >>> combien_b_dans_a(56,7)
- 8
- >>> combien_b_dans_a(55,7)
- 7
La fonction communique_combien_b_dans_a annonce le résultat, elle peut être occultée. Si elle est utilisée, elle nécessite la fonction combien_b_dans_a qu’elle appelle dans son code.
- def communique_combien_b_dans_a(a,b):
- n=combien_b_dans_a(a,b)
- if n*b==a:
- message=str(a)+" est un multiple de "+str(b)+", c'est "+str(n)+"*"+str(b)+"."
- else:
- message=str(n)+"*"+str(b)+" = "+str(n*b)+" est le plus grand multiple de "+str(b)+" inférieur à "+str(a)
- return message
Utilisation : Compiler le programme et taper dans la console
- >>> communique_combien_b_dans_a(58,7)
- '8*7 = 56 est le plus grand multiple de 7 inférieur à 58'
- >>> communique_combien_b_dans_a(57,7)
- '8*7 = 56 est le plus grand multiple de 7 inférieur à 57'
- >>> communique_combien_b_dans_a(56,7)
- "56 est un multiple de 7, c'est 8*7."
- >>> communique_combien_b_dans_a(55,7)
- '7*7 = 49 est le plus grand multiple de 7 inférieur à 55'
Déterminer si un entier naturel est premier.
On va ici s’autoriser à utiliser l’opération n%i qui donne le reste de la division euclidienne de $n$ par $i$. On utilisera aussi une liste « diviseurs » qui contiendra tous les diviseurs de $n$ (la méthode append permet d’ajouter un élément à une liste, len(liste) renvoie la longueur de liste).
- >>> diviseurs
- []
- >>> len(diviseurs)
- 0
- >>> diviseurs.append(1)
- >>> diviseurs
- [1]
- >>> len(diviseurs)
- 1
- >>> diviseurs.append(5)
- >>> diviseurs
- [1, 5]
- >>> len(diviseurs)
- 2
Si la liste des diviseurs est de longueur 2 c’est qu’elle contient 1 et $n$, donc que $n$ est premier.
- def estPremier(n):
- diviseurs=[]
- for i in range(1,n+1):
- if n%i==0:
- diviseurs.append(i)
- if len(diviseurs)==2:
- return True
- else:
- return False
Utilisation : Compiler le programme et taper dans la console
- >>> estPremier(47123)
- True
- >>> estPremier(471234567)
- False
On pourra diviser le temps de calcul par 2 avec le code suivant :
- def estPremier(n):
- diviseurs=[]
- borne_sup=round(n/2)
- for i in range(1,borne_sup+1):
- if n%i==0:
- diviseurs.append(i)
- diviseurs.append(n)
- if len(diviseurs)==2:
- return True
- else:
- return False
En effet, le plus grand diviseur de $n$, autre que $n$, ne peut pas être plus grand que $\frac n 2$.
Déterminer la première puissance d’un nombre positif donné supérieure ou inférieure à une valeur donnée.
L’énoncé ressemble beaucoup à « Pour des entiers a et b donnés, déterminer le plus grand multiple de $b$ inférieur ou égal à $a$. », on reprend donc ce script en l’adaptant au nouveau problème :
Le code Python pour $b^n \leq a$ :
- def puissance(a,b):
- m=b
- n=1
- while m<=a:
- if m==a:
- m*=b #Sert à arrêter le programme.
- return n
- else:
- m*=b
- n+=1 #Sert à compter l'exposant de b contenu dans a.
- if m!=a and m!=a*b:
- return n-1
- #Le résultat est n-1 puisque avec b^n on dépassait a
- return message
Utilisation :
- >>> puissance(1000,3)
- 6
- >>> puissance(243,3)
- 5
La fonction communique_puissance annonce le résultat, elle peut être occultée. Si elle est utilisée, elle nécessite la fonction puissance qu’elle appelle dans son code.
- def communique_puissance(a,b):
- n=puissance(a,b)
- if b**n==a:
- message=str(a)+" est une puissance de "+str(b)+", c'est "+str(b)+"^"+str(n)+"."
- else:
- message=str(b)+"^"+str(n)+" = "+str(b**n)+" est la plus grande puissance de "+str(b)+" inférieure à "+str(a)
- return message
Utilisation : Compiler le programme et taper dans la console
- >>> communique_puissance(1000,3)
- '3^6 = 729 est la plus grande puissance de 3 inférieure à 1000'
- >>> communique_puissance(243,3)
- "243 est une puissance de 3, c'est 3^5."
Et avec une toute petite modification, le code Python pour $b^n \geq a$ :
- def puissance(a,b):
- m=b
- n=1
- while m<=a:
- if m==a:
- m*=b #Sert à arrêter le programme.
- return n
- else:
- m*=b
- n+=1 #Sert à compter l'exposant de b contenu dans a.
- if m!=a and m!=a*b:
- return n
- #Le résultat est n puisque avec b^n on dépassait a
- return message
- def communique_puissance(a,b):
- n=puissance(a,b)
- if b**n==a:
- message=str(a)+" est une puissance de "+str(b)+", c'est "+str(b)+"^"+str(n)+"."
- else:
- message=str(b)+"^"+str(n)+" = "+str(b**n)+" est la plus petite puissance de "+str(b)+" supérieure à "+str(a)
- return message
Utilisation :
- >>> puissance(1000,3)
- 7
- >>> puissance(243,3)
- 5
- >>> communique_puissance(1000,3)
- '3^7 = 2187 est la plus petite puissance de 3 supérieure à 1000'
- >>> communique_puissance(243,3)
- "243 est une puissance de 3, c'est 3^5."
Géométrie
Étudier l’alignement de trois points dans le plan.
On va utiliser le fait que trois points sont alignés si ils ont la même abscisse, ou si ils ont la même ordonnée, ou si les coefficients directeurs de deux des droites qu’ils forment sont égaux. Comme le calcul des coefficients directeurs fait appel à un quotient, il faudra prendre la précaution d’éviter la division par 0.
Pour cet algorithme, nous avons fait le choix d’une fonction qui a en paramètres les noms des points suivis de leurs coordonnées, et qui retourne une phrase qui donne l’équation de la droite lorsque les points sont alignés, d’autres choix peuvent être faits. Cette phrase est une concaténation de chaînes de caractères, ceci se fait tout simplement avec le signe « + », les nombres sont transformés en chaînes de caractères à l’aide de la fonction str().
Ce programme détermine si trois points dont vous allez donner le nom et les coordonnées, sont alignés :
– Ne pas tester en même temps x_A==x_B et x_A==x_C permet d’éviter la division par 0 dans le calcul de m1, le coefficient directeur de (AB)
– m2 est le coefficient directeur de (AC)
– p est l’ordonnée à l’origine de (AB)
- def pts_alignes(A,B,C):
- pt_1,x_A,y_A=A
- pt_2,x_B,y_B=B
- pt_3,x_C,y_C=C
- if x_A==x_B:
- if x_A==x_C:
- message=pt_1+", "+pt_2+" et "+pt_3+" sont alignés et appartiennent à la droite verticale d'équation x = "+str(x_A)+"."
- else:
- message=pt_1+", "+pt_2+" et "+pt_3+" ne sont pas alignés."
- elif y_A==y_B and y_A==y_C:
- message=pt_1+", "+pt_2+" et "+pt_3+" sont alignés et appartiennent à la droite horizontale d'équation y = "+str(y_A)+"."
- else:
- if x_A==x_C:
- #permet d'éviter la division par 0 dans le calcul de m2
- message=pt_1+", "+pt_2+" et "+pt_3+" ne sont pas alignés."
- else:
- m1=(y_B-y_A)/(x_B-x_A)
- m2=(y_C-y_A)/(x_C-x_A)
- if m1==m2:
- p=y_A-m1*x_A
- message=pt_1+", "+pt_2+" et "+pt_3+" sont alignés et appartiennent à la droite oblique d'équation y = "+str(m1)+"x +"+str(p)+"."
- else:
- message=pt_1+", "+pt_2+" et "+pt_3+" ne sont pas alignés."
- return message
Utilisation :
- >>> pts_alignes(("D",2,-5),("E",3,-5),("F",4,-5))
- "D, E et F sont alignés et appartiennent à la droite horizontale d'équation y = -5."
- >>> pts_alignes(("D",2,-5),("E",2,-6),("F",4,-7))
- 'D, E et F ne sont pas alignés.'
- >>> pts_alignes(("D",2,-5),("E",2,-6),("F",2,-7))
- "D, E et F sont alignés et appartiennent à la droite verticale d'équation x = 2."
- >>> pts_alignes(("D",2,-5),("E",3,-6),("F",4,-7))
- "D, E et F sont alignés et appartiennent à la droite oblique d'équation y = -1.0x +-3.0."
- >>> pts_alignes(("D",2,-5),("E",3,-6),("F",3,-7))
- 'D, E et F ne sont pas alignés.'
Si l’on veut juste savoir si les points sont alignés ou pas :
- def pts_alignes(A,B,C):
- pt_1,x_A,y_A=A
- pt_2,x_B,y_B=B
- pt_3,x_C,y_C=C
- if x_A==x_B:
- #Ne pas tester en même temps x_A==x_B et x_A==x_C permet d'éviter la division par 0 dans le calcul de m1
- if x_A==x_C:
- return True
- else:
- return False
- elif y_A==y_B and y_A==y_C:
- return True
- else:
- if x_A==x_C:
- #permet d'éviter la division par 0 dans le calcul de m2
- return False
- else:
- m1=round((y_B-y_A)/(x_B-x_A),10)
- m2=round((y_C-y_A)/(x_C-x_A),10)
- if m1==m2:
- return True
- else:
- return False
- return message
Utilisation :
- >>> pts_alignes(("D",2,-5),("E",2,6),("F",1,-5))
- False
- >>> pts_alignes(("D",2,-5),("E",3,-6),("F",4,-7))
- True
A noter que l’on utilise round pour éviter que le test ne fonctionne pas à cause des flottants alors que m1==m2, voir ci-dessous :
- >>> pts_alignes(("D",0.2,-3.2),("E",0.3,-3.3),("F",0.4,-3.4))
- m1 = -0.9999999999999967
- m2 = -0.9999999999999987
- False
Déterminer une équation de droite passant par deux points donnés.
Pour cet algorithme, nous avons fait le choix, dans un premier temps, d’une fonction qui a en paramètres les coordonnées des deux points, et qui retourne une phrase qui donne l’équation réduite de la droite, d’autres choix peuvent être faits. Cette phrase est une concaténation de chaînes de caractères, ceci se fait tout simplement avec le signe « + », les nombres sont transformés en chaînes de caractères à l’aide de la fonction str().
Calcul de l’équation réduite d’une droite à partir des coordonnées de deux de ses points donnés sous la forme de couples (tuples).
- def EQ_DROITE(A,B):
- x_A,y_A=A
- x_B,y_B=B
- if x_A==x_B:
- if y_A==y_B:
- droite="Il n'est pas possible de donner UNE équation de droite avec deux points identiques !"
- else:
- droite="Droite verticale d'équation x = "+str(x_A)+"."
- elif y_A==y_B:
- droite="Droite horizontale d'équation y = "+str(y_A)+"."
- else:
- m=(y_B-y_A)/(x_B-x_A)
- p=y_A-m*x_A
- droite="Droite oblique d'équation y = "+str(m)+"x + "+str(p)+"."
- return droite
Utilisation :
- >>> EQ_DROITE((2,3),(2,33))
- "Droite verticale d'équation x = 2."
- >>> EQ_DROITE((2,3),(2,3))
- 'Il n'est pas possible de donner UNE équation de droite avec deux points identiques !'
- >>> EQ_DROITE((2,3),(-1,3))
- "Droite horizontale d'équation y = 3."
- >>> EQ_DROITE((2,3),(-1,-9))
- "Droite oblique d'équation y = 4.0x + -5.0."
Puisqu’il y a dans les nouveaux programmes de seconde :
– Équation de droite : équation cartésienne, équation réduite.
On peut proposer que la fonction renvoie une équation cartésienne de la droite (ou plus exactement ses coefficients) dans un tuple.
On pourra demander à l’élève de démontrer que si $M(x ; y) \in (AB)$, la colinéarité des vecteurs $\overrightarrow{AB}$ et $\overrightarrow{AM}$ mène à l’équation $(y_A-y_B)x + (x_B-x_A)y+x_A(y_B-y_A)+y_A(x_A-x_B)=0$ :
Calcul d’une équation cartésienne d’une droite à partir des coordonnées de deux de ses points donnés sous forme de couples (tuples).
Pour une équation cartésienne ax + by + c = 0, le programme renvoie (a,b,c).
- def EQ_CARTESIENNE(A,B):
- x_A,y_A=A
- x_B,y_B=B
- if A==B:
- return None
- else:
- return y_A-y_B,x_B-x_A,x_A*(y_B-y_A)+y_A*(x_A-x_B)
Utilisation :
- >>> EQ_CARTESIENNE((1,3),(1,3))
- #pas d'équation cartésienne.
- >>> EQ_CARTESIENNE((-5,3),(1,3))
- (0, 6, -18) #Équation 6y - 18 = 0
- >>> EQ_CARTESIENNE((-5,3),(-5,-3))
- (6, 0, 30) #Équation 6x + 30 = 0
- >>> EQ_CARTESIENNE((-5,3),(-2,-3))
- (6, 3, 21) #Équation 6 x + 3y + 21 = 0
Fonctions
Pour une fonction dont le tableau de variations est donné, algorithmes d’approximation numérique d’un extremum (balayage, dichotomie).
Nous commencerons par une fonction dont on sait qu’elle est croissante puis décroissante sur un intervalle [a ; b], donc qu’elle admet un maximum sur cet intervalle.
Nous procéderons d’abord par balayage :
- def f(x):
- return -3*x**3+4*x+1 #Entrez ici l'expression de la fonction
- def balayage(f,a,b,n):
- #n est le nombre de chiffres après la virgule souhaités
- e=10**(-n)
- #e est la précision souhaitée
- #a est la Borne inférieure de l'intervalle dans lequel le maximum a été repéré
- #b est la Borne supérieure de l'intervalle dans lequel le maximum a été repéré
- maxi=f(a)
- while b-a>e:
- amplitude=b-a
- for i in range(11): #On découpe arbitrairement [a ; b] en 10
- x=a+amplitude/10*i #calcul des abscisses obtenues en divisant [a ; b] en 10
- if f(x)>maxi:
- maxi=f(x)
- x_max=x
- a=x_max-amplitude/10 #On réduit l'intervalle dans lequel on va appliquer
- b=x_max+amplitude/10 #à nouveau le balayage jusqu'à la précision demandée
- return [round(a,n),round(b,n)]
Utilisation :
- >>> balayage(f,0,2,3)
- [0.666, 0.667]
- >>> balayage(f,0,2,7)
- [0.6666666, 0.6666667]
- >>> balayage(f,0,2,10)
- [0.6666666598, 0.6666666599]
A noter que Python montre ses limites à partir de n=8, la réponse [0.6666666598, 0.6666666599] est fausse puisque 2/3 n’appartient pas à cet intervalle ...
Par dichotomie, il y a un problème du fait que l’on ne sait pas dans quel intervalle se trouve le maximum : les données de f(a), f(m) et f(b) ne permettent pas de déterminer si le minimum est dans [a ; m] ou dans [m ; b]. Les deux cas sont possibles.
Plutôt que de couper l’intervalle en deux, nous allons le couper en trois, en posant $m = \frac{b -a}{3}$, $c = a + m$ et $d = b - m$.
On a alors a < c < d < b et l’on peut procéder par disjonction des cas, en notant $x_0$ l’abscisse du sommet :
– Si $a < x_0 <= c$, alors $f(c) > f(d)$.
– Si $c <= x_0 <= d$, alors $f(c) >= f(d)$ ou $f(c) <= f(d)$.
– Si $d <= x_0 <= b$, alors $f(c) < f(d)$.
L’on peut résumer cela en :
– Si $f(c) <= f(d)$ alors $x_0 \in [c ; b]$
– Si $f(c) >= f(d)$ alors $x_0 \in [a ; d]$
D’où l’algorithme suivant :
- def f(x):
- return -3*x**3+4*x+1 #Entrez ici l'expression de la fonction
- def trichotomie(f,a,b,n):
- #n est le nombre de chiffres après la virgule souhaités
- e=10**(-n)
- #e est la précision souhaitée
- #a est la Borne inférieure de l'intervalle dans lequel le maximum a été repéré
- #b est la Borne supérieure de l'intervalle dans lequel le maximum a été repéré
- while b-a>e:
- m=(b-a)/3
- c=a+m
- d=b-m
- if f(c)<=f(d):
- a=c
- else:
- b=d
- return (round(a,n),round(b,n))
Utilisation :
- >>> trichotomie(f,0,2,7)
- (0.6666666, 0.6666667)
- >>> trichotomie(f,0,2,8)
- (0.66666667, 0.66666667)
- >>> trichotomie(f,0,2,9)
- (0.666666674, 0.666666675)
- >>> trichotomie(f,0,2,10)
- (0.6666666743, 0.6666666744)
Où l’on constate les mêmes problèmes que dans le script précédent.
Avec une fonction dont on sait qu’elle est décroissante puis croissante sur un intervalle [a ; b], donc qu’elle admet un minimum sur cet intervalle.
Le code Python pour approcher le minimum par balayage :
- def f(x):
- return -3*x**3+4*x+1 #Entrez ici l'expression de la fonction
- def balayage_min(f,a,b,n):
- #n est le nombre de chiffres après la virgule souhaités
- e=10**(-n)
- #e est la précision souhaitée
- #a est la Borne inférieure de l'intervalle dans lequel le maximum a été repéré
- #b est la Borne supérieure de l'intervalle dans lequel le maximum a été repéré
- mini=f(a)
- while b-a>e:
- amplitude=b-a
- for i in range(11): #On découpe arbitrairement [a ; b] en 10
- x=a+amplitude/10*i #calcul des abscisses obtenues en divisant [a ; b] en 10
- if f(x)<mini:
- mini=f(x)
- x_min=x
- a=x_min-amplitude/10 #On réduit l'intervalle dans lequel on va appliquer
- b=x_min+amplitude/10 #à nouveau le balayage jusqu'à la précision demandée
- return (round(a,n),round(b,n))
Utilisation :
- >>> balayage_min(f,-2,1,5)
- (-0.66667, -0.66666)
- >>> balayage_min(f,-2,1,7)
- (-0.6666667, -0.6666666)
- >>> balayage_min(f,-2,1,8)
- (-0.66666667, -0.66666667)
Et par trichotomie :
- On entre l’expression de la fonction dans f(x)
– n est le nombre de chiffres après la virgule souhaités
– e est la précision souhaitée
– a est la Borne inférieure de l’intervalle dans lequel le maximum a été repéré
– b est la Borne supérieure de l’intervalle dans lequel le maximum a été repéré
- def f(x):
- return -3*x**3+4*x+1
- def trichotomie_min(f,a,b,n):
- e=10**(-n)
- while b-a>e:
- m=(b-a)/3
- c=a+m
- d=b-m
- if f(d)<=f(c):
- a=c
- else:
- b=d
- return (round(a,n),round(b,n))
Utilisation :
- >>> trichotomie_min(f,-2,1,7)
- (-0.6666667, -0.6666666)
- >>> trichotomie_min(f,-2,1,8)
- (-0.66666667, -0.66666666)
- >>> trichotomie_min(f,-2,1,9)
- (-0.666666664, -0.666666663)
Algorithme de calcul approché de longueur d’une portion de courbe représentative de fonction.
Ici, on veut approcher une fonction continue sur un intervalle [a ; b] par n segments dont les abscisses des extrémités sont distantes de (b-a)/n :
- from math import sqrt
- def f(x):
- return x**2 #Modifier la fonction ici
- def distance(x1,y1,x2,y2):
- return sqrt((x1-x2)**2 + (y1-y2)**2)
- def longueurCourbe(f,a,b,n):
- longueur = 0
- x1,y1 = a,f(a)
- h = (b-a)/n
- for i in range(n):
- x2 = x1 + h
- y2 = f(x2)
- longueur = longueur + distance(x1,y1,x2,y2)
- x1,y1 = x2,y2
- return longueur
Utilisation :
- >>> longueurCourbe(f,1,10,50)
- 99.5680744899207
- >>> longueurCourbe(f,1,10,100)
- 99.56828473103201
Python, le tableur ou la calculatrice, pour mettre en évidence l’aspect de programme de calcul.
Au lieu de proposer un programme de calcul basique que l’on trouve dans les manuels scolaires, intéressons-nous à un programme de calcul donné par un « magicien » dans une émission de télévision :
- def programme(n):
- N=n-n//10 #on enlève le chiffre des dizaines
- N=N-n%10 #on enlève le chiffre des unités
- return N
Utilisation :
- >>> programme(34)
- 27
- >>>from random import randint
- >>> programme(randint(10,99))
- 9
- >>> programme(randint(10,99))
- 81
- >>> programme(randint(10,99))
- 81
- >>> programme(randint(10,99))
- 63
On peut alors conjecturer que le nombre obtenu est toujours égal à 9 fois le chiffre des dizaines, ou plus généralement à un multiple de 9, le démontrer, puis démasquer le « truc » du « magicien ».
Exploiter un logiciel de géométrie dynamique ou de calcul formel, la calculatrice ou Python pour décrire les variations d’une fonction donnée par une formule.
On sait qu’une fonction f est croissante sur un intervalle [a ; b] si :
pour tous x et y de [a ; b], si x < y alors f(x) < f(y).
On sait qu’une fonction f est décroissante sur un intervalle [a ; b] si :
pour tous x et y de [a ; b], si x < y alors f(x) > f(y).
On peut donc imaginer de tester « pour tous » x et y de [a ; b], si f(x) < f(y), évidemment ici le "pour tous" va être remplacé par "un certain nombre de" ...
On peut alors avoir l’idée de mettre un "m" quand f(x) < f(y) (ça "monte") et un "d" quand f(x) > f(y) (ça « descend »), comme dans le script suivant :
- def f(x):
- return x**3-x**2-12*x-1 #Saisir ici l'expression de la fonction
- def sens_variation(a,b,f):
- pas=(b-a)/10
- tableau=[] #On crée une liste qui va stocker les résultats
- for i in range(10):
- x1=a+i*pas
- x2=x1+pas
- y1=f(x1)
- y2=f(x2)
- if y1<y2:
- tableau.append("m")
- else:
- tableau.append("d")
- return tableau
Utilisation :
- >>> sens_variation(-5,5,f)
- ['m', 'm', 'm', 'd', 'd', 'd', 'd', 'm', 'm', 'm']
Ce qui peut permettre de conjecturer que f est croissante puis décroissante puis croissante sur [-5 ; 5] ...
On peut affiner la conjecture en modifiant l’algorithme de manière à afficher les abscisses, on peut ainsi insérer avant le test if y1<y2 : :
- tableau.append(x2)
On obtient alors :
[-4.0, ’m’, -3.0, ’m’, -2.0, ’m’, -1.0, ’d’, 0.0, ’d’, 1.0, ’d’, 2.0, ’d’, 3.0, ’m’, 4.0, ’m’, 5.0, ’m’] ce qui peut permettre de conjecturer que f est croissante puis décroissante puis croissante, le changement de sens de variation se faisant entre -3 et -1 puis entre 1 et 3 (Attention ici à ne pas croire que ces changements se passent dans [-2 ; -1] et dans [2 ; 3], cela n’est pas vrai en général, nous vous laissons y réfléchir avec ces deux images ...).
On peut ensuite affiner :
tableau = [-2.8, ’m’, -2.6, ’m’, -2.4, ’m’, -2.27, ’m’, -2.0, ’m’, -1.8, ’m’, -1.6, ’m’, -1.4, ’d’, -1.2, ’d’, -1.0, ’d’]
tableau = [-1.76, ’m’, -1.72, ’m’, -1.68, ’m’, -1.64, ’d’, -1.6, ’d’, -1.56, ’d’, -1.52, ’d’, -1.48, ’d’, -1.44, ’d’, -1.4, ’d’]
tableau = [-1.712, ’m’, -1.704, ’m’, -1.696, ’m’, -1.688, ’d’, -1.68, ’d’, -1.672, ’d’, -1.664, ’d’, -1.656, ’d’, -1.648, ’d’, -1.64, ’d’]
pour en déduire que le premier changement de sens de variation se passe dans [-1.704 ; -1.688], en réalité en $\frac{2-\sqrt{148}}{6}\approx-1.694$.
Statistique et probabilités
Pour des données réelles ou issues d’une simulation, lire et comprendre une fonction écrite en Python renvoyant la moyenne m, l’écart type s, et la proportion d’éléments appartenant à [m-2s,m+2s].
On a besoin de calculer une racine carrée pour obtenir l’écart-type, ceci n’est pas possible dans Python sauf à importer cette fonction depuis un fichier math.py qui contient de nombreuses fonctions mathématiques, cela se fait à l’aide de la ligne suivante : from math import sqrt
- from math import sqrt
- def stats(tableau):
- n=len(tableau) #On mesure la taille du tableau
- m=sum(tableau)/n #calcul de la moyenne
- tableau2=[(x-m)**2 for x in tableau] #On crée le tableau des carrés des écarts à la moyenne
- variance=sum(tableau2)/n #Calcul de la variance
- s=sqrt(variance) #Calcul de l'ecartype
- a=m-2*s
- b=m+2*s
- compteur=0
- for i in range(n):
- if tableau[i]<=b and tableau[i]>=a:
- compteur+=1
- proportion=compteur/n
- return [m,s,proportion] #On renvoie un tableau qui contient la moyenne, l'écart-type et la proportion d’éléments appartenant à [m-2s,m+2s].
Après avoir compilé le code, on peut taper dans la console :
- >>> tableau=[i**2 for i in range(100)]
- >>> stats(tableau)
- >>> [3283.5, 2953.2966410436998, 0.96]
On peut compléter ce qui précède avec l’ensemble des fonctions qui donnent toutes les caractéristiques d’une série de données ’a 1 variable :
- from math import sqrt
- def mini(tab):
- minimum=tab[0]
- for element in tab:
- if element<minimum:
- minimum=element
- return minimum
- def maxi(tab):
- maximum=tab[0]
- for element in tab:
- if element>maximum:
- maximum=element
- return maximum
- def somme(tab):
- somme=0
- for element in tab:
- somme+=element
- return somme
- def somme_des_carres(tab):
- somme=0
- for element in tab:
- somme+=element**2
- return somme
- def moyenne(tab):
- return somme(tab)/len(tab)
- def etendue(tab):
- return maxi(tab)-mini(tab)
- def effectif_total(tab):
- return len(tab)
- def variance(tab):
- somme=0
- moy=moyenne(tab)
- for element in tab:
- somme+=(element-moy)**2
- return somme/len(tab)
- def ecart_type(tab):
- return sqrt(variance(tab))
- def ecart_type_echantillon(tab):
- L=len(tab)
- return sqrt(variance(tab)*L/(L-1))
- def tri_croissant(tab):
- sortie=tab
- sortie.sort()
- return sortie
- def Q1(tab):
- L=len(tab)
- if L%2==0:
- return tri_croissant(tab)[len(tab)//4-1]
- else:
- return tri_croissant(tab)[len(tab)//4]
- def Q3(tab):
- L=len(tab)
- if L%2==0:
- return tri_croissant(tab)[3*len(tab)//4-1]
- else:
- return tri_croissant(tab)[3*len(tab)//4]
- def mediane(tab):
- L=len(tab)
- sortie=tri_croissant(tab)
- if L%2==0:
- return moyenne([sortie[L//2-1],sortie[L//2]])
- else:
- return sortie[L//2]
- def ecart_interquartile(tab):
- return Q3(tab)-Q1(tab)
- def Stats_1Var(tab):
- print("Effectif total :"+str(effectif_total(tab)))
- print("Minimum :"+str(mini(tab)))
- print("Maximum :"+str(maxi(tab)))
- print("Étendue :"+str(etendue(tab)))
- print("Moyenne :"+str(moyenne(tab)))
- print("Écart type :"+str(ecart_type(tab)))
- print("Variance :"+str(variance(tab)))
- print("Premier quartile :"+str(Q1(tab)))
- print("Troisième quartile :"+str(Q3(tab)))
- print("Médiane :"+str(mediane(tab)))
- print("Écart interquartile :"+str(ecart_interquartile(tab)))
- print("Somme :"+str(somme(tab)))
- print("Somme des carrés :"+str(somme_des_carres(tab)))
- print("Écart type échantillon :"+str(ecart_type_echantillon(tab)))
- def boite_a_moustaches(tab):
- import matplotlib.pyplot as plt
- plt.clf()
- plt.boxplot([mini(tab),Q1(tab),mediane(tab),Q3(tab),maxi(tab)])
- plt.ylim(mini(tab)-2,maxi(tab)+2)
- plt.savefig('SimpleBoxPlot.png')
- plt.show()
Après avoir compilé le code, on peut taper dans la console :
- >>> Stats_1Var([6, 47, 49, 15, 43, 41, 7, 39, 43, 41, 36])
- Effectif total :11
- Minimum :6
- Maximum :49
- Étendue :43
- Moyenne :33.36363636363637
- Écart type :15.233173890455644
- Variance :232.04958677685954
- Premier quartile :15
- Troisième quartile :43
- Médiane :41
- Écart interquartile :28
- Somme :367
- Somme des carrés :14797
- Écart type échantillon :15.97668756202441
- >>> boite_a_moustaches([6, 47, 49, 15, 43, 41, 7, 39, 43, 41, 36])
On retrouve bien :
- Le minimum : 6
- Le premier quartile : 15
- La médiane : 41
- Le troisième quartile : 43
- Le maximum : 49
Lire et comprendre une fonction Python renvoyant le nombre ou la fréquence de succès dans un échantillon de taille n pour une expérience aléatoire à deux issues.
- from random import*
- def nb_succes(n,p):
- #n est le nombre de simulations
- #p est la probabilité testée
- c=0
- for k in range(1,n+1):
- t=random()
- if t<p:
- c=c+1
- return c
Utilisation :
- >>> nb_succes(1000,0.3)
- 303
- >>> 303/1000
- 0.303
On peut calculer la fréquence dans la console comme ci-dessus ou changer la dernière ligne du programme :
- return c/n
ou pour récupérer nombre et fréquence :
- return [c,c/n]
Observer la loi des grands nombres à l’aide d’une simulation sur Python ou tableur.
En statistiques, la loi des grands nombres exprime le fait que les caractéristiques d’un échantillon aléatoire se rapprochent des caractéristiques statistiques de la population lorsque la taille de l’échantillon augmente. Autrement dit la fréquence de succès d’un événement sur l’échantillon s’approche de la probabilité de cet événement sur la population.
Nous choisissons d’illustrer cela avec un jeu de dé :
On lance un dé à 6 faces non truqué.
– Si c’est 1 qui sort, on gagne 1€.
– Si c’est 2, 3 ou 4 qui sort, on gagne 2€.
– Sinon, on gagne 4€.
L’espérance de gain à ce jeu est donc de $\frac{1 \times 1 + 3\times 2 + 2\times 4}{6} = 2,5€$.
On utilisera deux fonctions :
– Une fonction expérience() qui simule un jeu.
– Une fonction evolutionMoyenne(experience,nExperiences) qui va jouer nExperiences fois le jeu, en calculant la moyenne à chaque nouveau jeu et en représentant graphiquement l’évolution de cette moyenne à l’aide de la librairie matplotlib :
- import matplotlib.pyplot as plt
- from random import randint
- def experience():
- de = randint(1,6)
- # on tire au hasard un nombre entier parmi 1,2,3,4,5,6
- if de < 2:
- X = 1
- elif de < 5:
- X = 2
- else:
- X = 4
- return X
- def evolutionMoyenne(experience,nExperiences):
- s = experience()
- n = 1
- L = [s] # moyenne sur 1 expérience
- while n < nExperiences:
- n = n+1
- s = s + experience()
- L.append(s/n) # on ajoute la moyenne sur n expériences
- plt.plot(list(range(1,nExperiences+1)),L,'b.')
- plt.plot([1,nExperiences],[2.5, 2.5],'r-')
- #2.5 est l'espérance de gain
- plt.grid()
- plt.show()
Utilisation :
- evolutionMoyenne(experience,500)
Simulation de 100 jeux : nExperiences=100 | Simulation de 1000 jeux : nExperiences=1000 |
Simulation de 2000 jeux : nExperiences=2000 | Simulation de 5000 jeux : nExperiences=5000 |
Bibliographie :
- Les algorithmes du programme de Mathématiques de Première technologique (2019) ;
- Les algorithmes et les scripts en Python pour la spécialité Mathématique de Première générale (2019) ;
- Quelques précisions données par l’Inspection Générale concernant l’écriture des programmes Python au lycée.
- Algorithmique et programmation au cycle 4, Commentaires et recommandations du groupe Informatique de la CII Lycée
- Les articles sur Scratch dans MathémaTICE
- Les articles sur Python dans MathémaTICE
- Parcours Magistère : Algorithmique/Programmation au Lycée : Python
- Passer de la seconde à la vitesse supérieure en Python, Olivier Le Goaër écrit au sujet de « Déterminer une équation de droite passant par deux points donnés. ».
- Représentation binaire des Nombres Réels