IPT 2e année 2016-17 Lycée Victor Hugo Table des matières I Les algorithmes à connaitre Informatique pour tous Deuxième année 2016-2017 Utilisation du polycopié : l’objectif de ce polycopié est de vérifier que vous maitrisez les algorithmes au programme. Attention : ce n’est pas tout le cours d’informatique. Munissez-vous d’une feuille et d’un sytlo puis commencez par lire la table des matières. Pour chaque algorithme : – Donnez-en une description succinte en français. Forcez-vous à l’écrire en toute lettre. – Traduisez ce qui vous avez écrit en langage Python. – Donnez une estimation de la complexité de l’algorithme. Si vous butez sur une de ces étapes, alors seulement, vous irez lire la partie correspondante. 1 Recherches et calculs dans une liste 1 Recherche d’un élément dans une liste . . . . . . . . . . . . 2 Recherche du minimum dans une liste . . . . . . . . . . . . 3 Calcul de la valeur moyenne et de la variance d’une liste 4 Recherche dichotomique dans une liste triée . . . . . . . . 5 Recherche de motifs dans une chaîne de caractères . . . . . . . . . 2 2 2 2 3 4 II Pivot de Gauss 1 Échelonnement du système . . . . . . . . . . . . . . . . . . . . . . . . . 2 Réduction du système . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Implémentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 5 5 6 III Recherche des zéros d’une application 1 Dichotomie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Méthode de Newton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 8 9 IV Intégration numérique 1 Méthode des rectangles . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Méthode des trapèzes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 10 10 V Résolution d’équation différentielle 1 Équation différentielle d’ordre 1 . . . . . . . . . . . . . . . . . . . . . . 2 Équations différentielles d’ordre 2 . . . . . . . . . . . . . . . . . . . . . 10 10 11 VI Algorithmes de tris 1 Le tri par insertion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Le tri fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Le tri quicksort (ou tri rapide) . . . . . . . . . . . . . . . . . . . . . . . 12 12 12 13 . . . . . . . . . . . . . . . . . . . . . . . . . IPT 2e année 2016-17 Lycée Victor Hugo I Recherches et calculs dans une liste 1 Recherche d’un élément dans une liste Néanmoins, si dans un sujet de concours de on vous demander de coder la recherche d’un élément dans une liste ce n’est sans doute pas cette méthode qui est attendue. Script 1 H Soit L une liste d’éléments : chaque élément de L peut être accédé par son indice i ∈ J0, n−1K. Ainsi : L[i] représente l’élément d’indice i. On veut savoir si un objet x est élément de L. Pour cela, on peut parcourir toute la liste et tester si l’élément d’indice i est égal à x. Cette algorithme possède une complexité temporelle linéaire. Voici un exemple de fonction qui renvoie True ou False selon que x appartient ou non à L. On suppose que L contient au moins 1 élément. Programmer deux fonctions (itératives et récursives) recherche_occurence (x,L) et recherche_occurence_rec(x,L) qui renvoient le nombre d’occurences de la valeur x dans une liste L. 2 def appartient_init (a , liste ) : " fonction bool é enne testant si un é l é ment x est dans une liste L donn é e " res = False for jj in range ( len ( liste ) ) : if liste [ jj ] == a : res = True return res Recherche du minimum dans une liste On suppose que les éléments de la liste L obéissent à une relation d’ordre total. Pour trouver le plus petit élément, l’idée est de créer une variable min_potentiel correspondant potentiellement au minimum et valant initalement L[0]. Puis, on parcourt la liste et on modifie min_potentiel à chaque fois qu’on trouve un élément qui lui est plus petit. Voici un exemple de fonction qui renvoie le plus petit élément d’une liste ainsi que son indice. On suppose que L contient au moins 1 élément. def minimum ( L ) : min_pot = L [0] ind_pot = 0 for i in range ( len ( L ) ) : if L [ i ] < min_pot : # on trouv é un é l é ment plus petit que le minimum potentiel min_pot = L [ i ] ind_pot = i return ( min_pot , ind_pot ) Une version récursive (de complexité linéaire) serait la suivante : def appartient_rec (x , L ) : " fonction bool é enne r é cursive testant si un é l é ment x est dans une liste L donn é e " n = len ( l ) if n == 0: return False # x n ’ est pas dans la liste Vide else : return x == L [0] or appartient_init (x , L [1:]) Script 2 H Programmer une fonction récursive max_rec(L) qui renvoie le maximum d’une liste et son indice. Remarque : La fonction in de Python permet également de savoir si élément appartient à une liste : 3 Calcul de la valeur moyenne et de la variance d’une liste Rappels : On considère une liste de N réels x1 , x2 , ..., xN la valeur moyenne m et la variance sont définies par : def appartient_bis (x , L ) : return ( x in L ) 2 IPT 2e année 2016-17 Lycée Victor Hugo – Si x = L[m] on a trouvé x – Sinon, si x < L[m] alors il faut chercher x entre debut et m. – Sinon, si x > L[m] alors il faut chercher x entre m et f in. – On itère ce processus jusqu’à ce que debut = f in La complexité d’un tel algorithme est logarithmique. Une possibilité de code itératif est le suivant : N 1 X m= xi N N 1 X et V = (xi − m)2 N i=1 i=1 P 2 2 En développant le terme (xi − m) , on montre que V = N1 N i=1 xi − m. Voici une façon de calculer la moyenne ( fonction moyenne(L)) et la variance ( fonction variance(L)) d’une liste L donnée en argument. def re ch er ch e_ di ch o_ it (L ,p , q x ) : ’’’ Recherche par dichotomie dans la liste tri é e L , l ’é l é ment x entre les indices p et q . ’’’ min = p max = q while min <= max : m = ( min + max ) //2 if x == L [ m ] : return True elif x < L [ m ] : max = m -1 else : min = m +1 return False def moyenne ( x ) : somme =0 for i in x : somme += i return somme / len ( x ) def variance ( x ) : xmoy = moyenne ( x ) ecartcarre =[] for i in x : ecartcarre . append (( i - xmoy ) **2) return moyenne ( ecartcarre ) Remarque : le module numpy possède des fonctions mean et var calculant la moyenne et la variance. Une possibilité de code récursif est le suivant : def appartient (x , L ) : " fonction bool é enne efficace testant si un é l é ment est dans une liste tri é e par ordre croissant donn é e " n = len ( L ) if n == 0: return False # a n ’ est pas dans la liste Vide elif n == 1: return x == l [0] # si la liste n ’a qu ’ un é l é ment , on teste si a est cet é l é ment else : m = n //2 Script 3 H Programmer une fonction kurtosis(L) qui renvoie le kurtosis de L, défini N 1 X xi − m 4 √ par K = N V i=1 4 Recherche dichotomique dans une liste triée Soit L une liste de nombres triés par ordre croissant. On veut savoir si un nombre x appartient à cette liste en utilisant le princippe de dichotomie suivant, en notant L[debut,fin] la sous-liste de L commençant à l’indice debut et fin : debut + f in – On calcule m = E( ), le milieu de la sous-liste. 2 3 IPT 2e année 2016-17 Lycée Victor Hugo if x == l [ m ]: return True elif a < l [ m ]: return appartient (a , l [: m ]) else : return appartient (a , l [ m +1:]) 5 j = 0 while ( j < p ) and ( texte [ i + j ] == motif [ j ] ) : j += 1 if j == p : res = True return Res Recherche de motifs dans une chaîne de caractères Script 4 H Modifier cette algorithme pour compter le nombre d’occurence d’un mot dans un texte. Soit deux chaînes de caractères : texte de taille n et motif de taille p < n. On veut savoir si motif apparaît dans texte. Le façon « naïve »de le faire consiste à faire glisser le motif le long de texte et de vérifier s’il y a coïncidence caractère par caractère. Par exemple, on cherche si aime est dans la phrase LVH aime Python. II La méthode du pivot de Gauss permet de résoudre un système linéaire de n équations et n inconnues. Un système linéaire de n équations à n inconnues s’écrit sous la forme suivante : a1,1 x1 + · · · + a1,n xn = b1 ··· (S) : an,1 x1 + · · · + an,n xn = bn Première itération : indices texte motif 0 L a 1 V i 2 H m 3 4 a 5 i 6 m 7 e 8 9 P 10 y 11 t 12 h 13 o 14 n e Le a de aime n’est pas égal au L de LVH. On décale le motif d’un cran à droite. Avec, ∀(i, j) ∈ J1, nK : – ai,j : les coefficients du système (S), – xi : les inconnues du système (S), – bj : les seconds membres du système (S). Le système d’équations linéaires (S) peut se mettre sous écriture matricielle : Cinquième itération : indices texte motif 0 L 1 V 2 H 3 4 a a 5 i i 6 m m 7 e e 8 9 P 10 y 11 t 12 h 13 o Pivot de Gauss 14 n Toutes les lettres a, i, m et e coïncident, on a trouvé le motif dans le texte. Cette algorithme possède une compléxité linéaire en n + p. Voici une façon de traduire cet algorithme en langage Python. def recherche ( texte , motif ) : n = len ( texte ) p = len ( motif ) res = False for i in range (n - p +1) : avec a1,1 a1,2 a2,1 a2,2 A= ··· ··· an,1 an,2 (S) ⇔ AX = B · · · a1,n x1 x2 · · · a2,n , X = · · · ··· ··· · · · an,n xn b1 b2 et B = · · · bn La résolution du système d’équations (S) par la méthode du pivot de Gauss se décompose en deux grandes étapes : 4 IPT 2e année 2016-17 Lycée Victor Hugo (b) On effectue des opérations Li ← Li +λL1 pour i allant de 2 à n de manière à faire disparaître x1 des équations 2 à n. 1. échelonnement du système (descente), 2. réduction du système (remontée). 2. Deuxième étape : On recommence les étapes (a) et (b) avec les lignes 2 à n et l’inconnue x2 (on cherche donc un pivot sur la 2ième colonne dans les lignes L2 à Ln ). Ces deux étapes d’échelonnement et de réduction du système sont réalisées en effectuant des opérations élémentaires sur les lignes, ce qui correspond à des opérations sur les équations : – multiplication d’une ligne par unscalaire non nul (Li ← λLi ) avec λ 6= 0, – ajout de λLi à Lj Lj ← Lj + λLi avec i 6= j, – échange de lignes Li ↔ Lj . 3. Étapes suivantes, jusqu’à la (n − 1)ième étape : On recommence le processus afin d’obtenir une matrice échelonnée (la ième étape consistant à chercher un pivot sur la ième colonne dans les lignes Li à Ln ). Chaque opération élémentaire étant inversible par une opération élémentaire (de même type), le système (S) et tout système (S’) obtenu à partir de (S) à l’aide d’opérations élémentaires sur les lignes, sont équivalents et admettent donc les mêmes solutions. Effectuer des opérations sur les lignes du système (S) revient à effectuer ces mêmes opérations sur les lignes de la matrice A et du vecteur B. Plutôt que d’effectuer chaque opération élémentaire sur A puis sur B, les opérations seront effectuées une seule fois sur une matrice que l’on nomme matrice augmentée du système (S). La matrice augmentée du système (S) est la matrice notée (A|B) : a1,1 a1,2 a2,1 a2,2 (A|B) = ··· ··· an,1 an,2 ··· ··· ··· ··· À l’issue de ces opérations, on type : a01,1 0 (A|B)0 = 0 · · · 0 a01,2 a02,2 0 ··· 0 ··· ··· ··· ··· ··· a01,n−1 a02,n−1 a03,n−1 ··· 0 a01,n b01 a02,n b02 a03,n b03 · · · · · · a0n,n b0n Cas d’une matrice A non inversible : s’il n’est pas possible de trouver un pivot (non nul) sur la ième colonne dans les lignes Li à Ln , la matrice A n’est pas inversible, on interrompt l’algorithme et on retourne un message d’erreur. a1,n b1 a2,n b2 · · · · · · an,n bn 2 Si la matrice A du système est inversible (système de Cramer) alors le système (S) AX = B admet donc une unique solution X = A−1 B. Dans le cas contraire, nous préciserons ce qui permet de repérer que la matrice A n’est pas inversible au cours du déroulement du programme. 1 aboutit à une matrice augmentée échelonnée du Réduction du système 1. Par multiplication de chaque ligne Li par en 1. 1 , a0i,i on change la valeur des pivots 2. La dernière ligne donne alors la valeur de xn . On utilise le pivot valant 1 sur la dernière ligne et la dernière colonne pour éliminer xn des équations L1 à Ln−1 par des opérations de la forme Li ← Li + λLj . Échelonnement du système 1. Première étape : 3. La ligne Ln−1 donne alors la valeur de xn−1 . On utilise le pivot valant 1 en position (n − 1, n − 1) pour éliminer xn−1 des équations L2 à Ln−2 . (a) On suppose que le premier coefficient de la première ligne est non nul (a1,1 6= 0, quitte à échanger deux lignes). Ce premier coefficient non nul s’appelle le premier pivot. 4. On fait de même jusqu’à la ligne L2 . 5 IPT 2e année 2016-17 Lycée Victor Hugo def Augmentation (A , B ) : ’’’ Fonction qui retourne de la matrice augment é e M =( A | B ) à partir de : A : matrice carr é e ( array nxn ) B : vecteur ( array nx1 ) ’’’ n = A . shape [0] # nombre de lignes de A nj = A . shape [1] # nombre de colonnes de A mi = B . shape [0] # nombre de lignes de B mj = B . shape [1] # nombre de colonnes de B if ( n == nj and n == mi ) : M = np . zeros ([ n , n + mj ]) M [: ,0: n ]= A [: ,:]. copy () M [: , n : n + mj ]= B [: ,:]. copy () return M else : print ( " Syst è me non valide " ) À l’issue de ces opérations, on aboutit à une matrice augmentée échelonnée et réduite du type : 1 0 (A|B)00 = 0 · · · 0 0 1 0 ··· 0 ··· ··· ··· ··· ··· 0 0 0 ··· 0 0 0 0 ··· 1 b001 b002 b003 · · · b00n On peut finalement extraire la matrice A00 et le second membre B 00 de la matrice augmentée (A|B)00 . La matrice A00 correspondant à l’identité, on a X = B 00 . 3 Implémentation Afin de coder le plus efficacement possible le pivot de Gauss, il ne faut pas hésiter à utiliser des fonctions intermédiaires qui exécutent des tâches simples. (C’est d’ailleurs un principe qu’il faut toujours se forcer à appliquer.) Manipulation des lignes : def PivotNonNul (M , i ) : ’’’ Fonction qui retourne le rang du premier pivot non nul de la colonne i du tableau M et renvoie un bool é en qui vaut True en cas d ’ existance de pivot non nul et False dans le cas contraire ’’’ reussi = True n = M . shape [0] rg_mp = i # indice du premier pivot val_mp = M [i , i ] # valeur du premier pivot while val_mp ==0 and rg_mp <n -1: rg_mp = rg_mp +1 val_mp = M [i , rg_mp ] # valeur du pivot if val_mp == 0: # print ( ’ Le syst è me n \ ’ est pas inversible ’) reussi = False return [ rg_mp , reussi ] # rang du premier pivot non nul pivot / r é ussi def EchangeLigne (M ,i , j ) : ’’’ Proc é dure qui é change en place , les lignes i et j du tableau M ’’’ M [i ,:] , M [j ,:]= M [j ,:]. copy () ,M [i ,:]. copy () def AjoutLigne (M ,i ,j , a ) : ’’’ Proc é dure qui ajoute en place à la ligne i du tableau M la ligne j multipli é e par scal . On suppose que i est diff é rent de j ’’’ M [i ,:]= M [i ,:]. copy () + a * M [j ,:]. copy () def MultiplieLigne (M ,i , scal ) : ’’’ Proc é dure qui multiplie en place la ligne i du tableau M par le scalaire scal . On suppose que scal est non nul ’’’ M [i ,:]= scal * M [i ,:]. copy () Les différentes étapes : 6 IPT 2e année 2016-17 Lycée Victor Hugo a = - M [ k ][ i ]/ M [ i ][ i ] AjoutLigne (M ,k ,i , a ) def Echelonnement ( M ) : """ Fonction / proc é dure qui impl é mente la phase de descente de la m é thode du pivot de Gauss , en place . Renvoie un bool é en qui vaut True en cas de r é ussite et False dans le cas contraire """ n = M . shape [0] for i in range (0 , n ) : # jusque n pour d é tecter si le dernier pivot est nul # place le meilleur pivot partiel de la colonne i [p , reussi ]= PivotNonNul (M , i ) if ( reussi == True ) : EchangeLigne (M ,i , p ) # puis é limine les termes en dessous du pivot de la colonne i for j in range ( i +1 , n ) : a = - M [j , i ]/ M [i , i ] AjoutLigne (M ,j ,i , a ) return reussi def ExtraireSolution ( M ) : ’’’ Fonction qui extrait le second membre de la matrice augment é e M ’’’ n = M . shape [0] m = M . shape [1] - n B2 = np . zeros ([ n , m ]) B2 [: ,:]= M [: , n : n + m ] return B2 La fonction principale : def Gauss (A , B ) : ’’’R é solution du syst è me lin é aire AX =B , par la m é thode du pivot de Gauss partiel . A : matrice carr é e ( array nxn ) B : vecteur des seconds membres ( array nx1 ) sortie : vecteur des inconnues X ( array nx1 ) ’’’ # cr é ation de la matrice augment é e ( A | B ) M = Augmentation (A , B ) # é chelonnement reussi = Echelonnement ( M ) # Normalisation / r é duction ( ou l ’ inverse ) Normalisation ( M ) Reduction ( M ) # extraction solution X = ExtraireSolution ( M ) return [X , reussi ] def Normalisation ( M ) : ’’’ Proc é dure qui normalise les pivots de M ’’’ n = M . shape [0] for i in range (0 , n ) : MultiplieLigne (M ,i ,1/ M [i , i ]) def Reduction ( M ) : ’’’R é duit la matrice ’’’ n = M . shape [0] for i in range (n -1 ,0 , -1) : # é limine les termes de M au dessus du pivot de la colonne i for k in range (i -1 , -1 , -1) : # Exemple test1 A1 = np . array ([[ -2 ,4 ,1] ,[8 ,2 , -1] ,[2 , -1 ,2]]) B1 = np . array ([[ -18] ,[6] ,[27]]) # solution : 3 , -5 ,8 [ X1 , inversible ]= Gauss ( A1 , B1 ) 7 IPT 2e année 2016-17 Lycée Victor Hugo III Recherche des zéros d’une application La borne droite fournit une valeur approchée par excès. Soit f : [a, b] → R une applications réelle (a > b), continue sur [a, b] et admettant au moins une valeur c ∈ [a, b] telle que f (c) = 0. On cherche des algorithme donnant une valeur approchée de c. Ces algorithmes seront surtout utiles lorsqu’on ne peut pas calculer de valeurs exactes. 1 Cf f (b) a Dichotomie c1 0 A b Le principe f (a) Le principe de la dichotomie repose sur : - le théorème des valeurs intermédiaires : Si f : [a; b] → R est continue sur le segment [a, b] et si f (a) et f (b) sont de signes contraires, alors l’équation f (x) = 0 admet au moins une solution dans l’intervalle [a, b] - le théorème de la valeur intermédiaire : Si f : [a; b] → R est continue et strictement monotone sur [a, b] et si f (a) et f (b) sont de signes contraires, alors l’équation f (x) = 0 admet une et une seule solution dans l’intervalle [a, b]. B Difficultés pratiques Les suites (gn )n∈N et (dn )n∈N obtenues par la méthode de dichotomie convergent toujours vers un zéro de la fonction f (sous réserve que f soit continue sur [a; b] et f (a) × f (b) < 0). Mais s’il y a plusieurs zéro sur l’intervalle considéré, on ne sait pas vers lequel les suites vont converger. Le seuil d’arrêt peut être difficile à estimer, d’autant que les calculs des valeurs prises par f sont nécessairement arrondies comme tout calcul avec des flottants. Pour obtenir des valeurs approchées d’une solution à l’équation f (x) = 0 dans l’intervalle [a, b], on peut procéder par dichotomie. La fonction f : [a; b] → R est supposée continue sur l’intervalle [a, b] et f (a) et f (b) sont supposés de signe contraire. - on part de l’intervalle [a, b] donc g0 = a et d0 = b (avec a < b) - à chaque étape, on évalue la fonction f au point milieu de l’intervalle [gn ; dn ] n i.e. en m = gn +d 2 · Si f (gn ) et f (m) sont de signe contraire, on se place sur l’intervalle [gn ; m] i.e. gn+1 = gn et dn+1 = m, car il contient une solution de l’équation f (x) = 0. Sinon f (dn ) et f (m) sont de signe contraire, et on se place sur [m; dn ] i.e. gn+1 = m et dn+1 = dn . - on s’arrête quand la précision est satisfaisante. À chaque étape, la longueur de l’intervalle est divisée par 2. La borne gauche gn est une valeur approchée par défaut de la solution recherchée c avec |gn − c| 6 |b − a| × 2−n . C Un exemple de programmation def zero_dicho (f , a , b , p ) : ’’’f fonction continue sur [ a ; b ] avec a < b f ( a ) et f ( b ) de signes contraires renvoie une valeur approch é e d ’ une solution de f ( x ) =0 dans [a , b ] à la pr é cision p ’’’ g = a # extr é mit é gauche d = b # extr é mit é droite while d - g >= p : # g < d m = ( g + d ) /2 if f ( m ) * f ( g ) <= 0: d = m else : 8 IPT 2e année 2016-17 Lycée Victor Hugo g = m return g 2 Méthode de Newton A Le principe On peut réitérer le procédé pour améliorer cette fois la précision de a0 : on obtient une nouvelle valeur approchée a00 , que l’on peut elle aussi affiner... et ainsi de suite aussi longtemps qu’on le souhaite. On construit ainsi une suite d’approximations (an )n∈N de la solution x ∗ qui vérifient : On cherche à résoudre numériquement une équation de la forme f (x) = 0 où f est une fonction d’une variable réelle, à valeurs réelles, assez régulière (i.e. au moins dérivable). On note x ∗ une solution de cette équation que l’on souhaite approcher. a0 = a On part de a, une valeur approchée assez grossière de x ∗ . Pour améliorer sa précision : - on trace la tangente à la courbe de f au point a ; - on repère le point d’intersection de cette tangente avec l’axe des abscisses ; - l’abscisse a0 de ce point est une nouvelle valeur approchée de x ∗ dont on espère qu’elle est plus précise que a ! 0 x∗ a1 B f (a0 ) f (an ) f 0 (an ) Exemple de programmation def Newton (f , fd , x0 , epsilon ) : ’’’ Recherche à espilon pr è s du z é ros de la fonction f , de d é riv é e fd , proche de x0 ’’’ x = x0 y = x0 - f ( x0 ) / fd ( x0 ) while abs ( y - x ) > epsilon : x = y y = x - f ( x ) / fd ( x ) return y L’équation de la tangente à la courbe au point d’abscisse a est :y = f (a)+f 0 (a) (x−a) Donc le point d’intersection de cette droite avec l’axe des abscisses a pour coordonnées (a0 , 0) avec : 0 = f (a) + f 0 (a) (a0 − a) d’où : a0 = a − ∀ n ∈ N, an+1 = an − • Calcul de la dérivée Les langages de programmation permettant rarement d’effectuer du calcul formel, il n’est guère envisageable de calculer automatiquement la dérivée d’une fonction donnée en un point. Ce problème peut être contourné de plusieurs manières : - Au cas par cas, on calcule l’expression de la dérivée f 0 à la main puis la code explicitement dans le programme (on définit la fonction f_prime comme on l’a fait auparavant pour la fonction f). - On peut aussi approcher f 0 (a) par un taux d’accroissement : f (a + h) − f (a) f 0 (a) ' où h est une constante « petite », fixée à l’avance. h Cf a0 et f (a) f 0 (a) 9 IPT 2e année 2016-17 Lycée Victor Hugo C La méthode des rectangles approxime Rb l’aire a f (x)dx sous la courbe par des rectangles. Vitesse de convergence Comparons les performances de la √ méthode de Newton et de la dichotomie pour obtenir des valeurs approchées de 2 ' 1.4142135623730951 : n 0 1 2 3 4 5 6 7 Méthode de Newton 2.0000000000000000 1.5000000000000000 1.4166666666666667 1.4142156862745099 1.4142135623746899 1.4142135623730951 1.4142135623730950 1.4142135623730951 (erreur) 5,9.10−1 8,6.10−2 2,5.10−3 2,1.10−6 1,6.10−12 < 10−16 −2,2.10−16 < 10−16 Méthode par dichotomie 1.0000000000000000 1.5000000000000000 1.2500000000000000 1.3750000000000000 1.4375000000000000 1.4062500000000000 1.4218750000000000 1.4140625000000000 Z (erreur) −4,1.10−1 8,6.10−2 −1,6.10−1 −3,9.10−2 2,3.10−2 −8,0.10−3 7,7.10−3 −1,5.10−4 a = a0 a1 a2 Rb Afin d’approximer 2 a a n−1 b−a X b−a f (x)dx ' ) f (a + k n n k=0 f à ε près, il faut choisir n tel que : n > Constante ε Méthode des trapèzes La méthode des trapèzes approxime l’aire Rb a f (x)dx sous la courbe Rpar des trapèzes. b Elle consiste à approcher a f par : On constate qu’ici la méthode de √ Newton converge beaucoup plus rapidement que la méthode par dichotomie, vers 2 : en 5 itérations seulement, on obtient la précision maximale autorisée par la représentation des flottants en double précision. Pendant ce temps, la dichotomie n’a produit qu’une seule décimale exacte. IV ... an−1b = an b n−1 a = a0 a1 a2 ... an−1b = an Intégration numérique Rb Afin d’approximer Rb En général, on ne sait pas calculer la valeur exacte d’une intégrale a f (x)dx. Il Rb faut donc trouver des moyens d’approximer a f (x)dx. Rb Idée : interpréter a f (x)dx comme l’aire sous la courbe de f , et l’approcher par l’aire de <<quelque chose>> qu’on sait calculer. Remarque : 1 1 V Méthode des rectangles a b−aX (f (ak ) + f (ak+1 )) 2n k=0 . q f à ε près, il faut choisir n tel que : n > Constante ε La méthode des trapèzes est plus efficace que celle des rectangles. Résolution d’équation différentielle Équation différentielle d’ordre 1 La méthode d’Euler permet de résoudre de manière approchée des équations différentielles présentées sous la forme ( y0 (t) = F t, y(t) , t ∈ [t0 , t0 + T ] y(t0 ) = y0 , 10 IPT 2e année 2016-17 Lycée Victor Hugo où F est une fonction de deux variables (t, y), définie au voisinage de (t0 ; y0 ). Ce cadre permet d’étudier la plupart des équations différentielles du premier ordre sur un intervalle de temps fini [t0 , t0 + T ], munie d’une condition initiale à l’instant t0 . Graphiquement, la méthode d’Euler consiste à approcher la courbe (le graphe) de la fonction solution g, inconnue, par une ligne brisée que l’on construit point après point (approximation affine par morceaux). On décompose l’intervalle d’étude en y répartissant n + 1 instants régulièrement espacés (donc il y a n intervalles de temps) : t0 < t1 < t2 < · · · < tn−1 < tn = t0 + T . Deux instants consécutifs sont donc séparés d’un même pas temporel h = Tn – La valeur de la solution en l’instant t0 est donnée par l’équation : c’est y0 . On partira donc du point M0 (t0 , y0 ) – Pour construire le point suivant, on utilise l’équation différentielle et on décide d’assimiler la courbe de y sur l’intervalle [t0 ; t1 ] à sa tangente en t0 : y(t1 ) = y(t0 ) + (t1 − t0 )F (t0 , y0 ) = y0 + hF (t0 , y0 ) – Et on réitère... jusqu’à arriver à l’instant tn : On approche les valeurs de la solution aux instants tk par les nombres yk calculés de manière récurrente par : ∀k ∈ J0, n − 1K, yk+1 = yk + hF (tk , yk ) l’espace des phases (comme en physique lorsque l’on trace un portrait de phase). Plus précisément, on prend comme inconnues non plus seulement la fonction mais la fonction et toutes ses dérivées successives jusqu’au l’ordre de l’équation différentielle moins 1. Dans le cas d’une équation différentielle 2, d’inconnue y à valeurs réelles d’ordre y(t) ~ 0 en fonction de t et ~ (t) = et d’exprimer u il suffit d’introduire le vecteur u y0 (t) ~. de u ~ (0) = u ~ 0 revient à donner la valeur en t0 de la fonction La condition initiale u inconnue y ainsi que de sa dérivée y0 à la même date. Un pendule libre non amortivérifie comme équation différentielle θ̈ + θ ~= , α représentant la vitesse angulaire ω02 sin(θ) = 0. Il se traite en posant u α θ̇ du pendule. On a alors Exemple : ~0 = u Les points Mk (tk , yk ) dessinent une ligne brisée qui approche la courbe solution sur l’intervalle [t0 , t0 + T ]. ~) = donc ici F~ (t, u α −ω02 sin(θ) La méthode d’Euler s’écrit donc dans ce cas : ( def euler (T , y0 , t0 , n ) : y = np . zeros ( n +1) y [0] = y0 t = t0 for k in range ( n ) : y [ k +1] = y [ k ] + T / n * F (y , t ) t = t0 + T / n return y 2 α θ̇ θ̇ θ̇ = = = −ω02 sin(θ) α̇ −ω02 sin(θ) θ̈ θ0 , α0 conditions initiales données et θk+1 = θk + h αk αk+1 = αk − ω02 sin(θk ) Ainsi, après application de la méthode d’Euler, on peut facilement tracer θ en fonction de la date t, mais aussi θ̇ en fonction de θ, i.e. la courbe dans l’espace des phases. Un code possible est le suivant : def euler_pendule (T , omega0 , theta0 , alpha0 , n ) : h = T/n thetak , alphak = theta0 , alpha0 theta = [ theta0 ] alpha = [ alpha0 ] for k in range ( n ) : Équations différentielles d’ordre 2 Les équations différentielles d’ordre 2 (ou plus) peuvent se traiter en se réécrivant sous forme d’un système différentiel vectoriel d’ordre 1. On pourra travailler dans 11 IPT 2e année 2016-17 Lycée Victor Hugo B thetakplus1 = thetak + alphak * h # attention à ne pas é crire thetak = thetak + alphak * h alphak = alphak - omega0 **2* np . sin ( thetak ) * h thetak = thetakplus1 theta . append ( thetak ) alpha . append ( alphak ) return ( theta , alpha ) def tri_insertion ( t ) : n = len ( t ) if n >1: for k in range (1 , n ) : a_placer = t [ k ] j =k -1 a_tester = True # pour ne pas sortir par la gauche while a_tester : if t [ j ] > a_placer : t [ j +1]= t [ j ] j =j -1 a_tester = j >=0 else : a_tester = False t [ j +1]= a_placer Remarques : La méthode d’Euler est peu utilisée en pratique car elle est lente et souvent instable. On lui préfère d’autres méthodes plus sophistiquées, notamment la méthode du point milieu ou plus généralement les méthodes de Runge-Kutta. Ces méthodes sont déjà implantées en Python, ainsi que d’autres très performantes : il suffit de charger la bibliothèque scipy en particulier son module integrate, et utiliser par exemple la fonction odeint. VI 1 Algorithmes de tris Le tri par insertion Le tri insertion est un algorithme efficace de tri pour un petit nombre d’éléments. Il est souvent utilisé pour trier un jeu de cartes, un paquet de copies. A Algorithme 2 Le tri fusion A Le principe Il s’agit d’utiliser la méthode "diviser pour régner" qui consiste ici à diviser le tableau en deux parties de même taille (à une unité près si besoin est), de trier (récursivement) ces deux parties et de fusionner les deux listes sous-triées en une seule liste triée. Comme à chaque étape, on diminue strictement la taille du tableau (partie de tableau) à trier, on finit toujours par obtenir un tableau vide ou de longueur 1. Pour fusionner en une seule liste, on parcourt les deux sous-listes triées (en faisant attention à ne pas sortir des tableaux) en ajoutant les éléments un à un. La complexité temporelle de l’algorithme de tri fusion est en O(n ln n) .Il n’y a pas de meilleur ou de pire des cas. Le principe Il consiste à trier la liste élément par élément, de manière à ce qu’à chaque instant le début de la liste soit triée (la fin de la liste ne servant que pour le stockage). A la k-ième étape, on suppose que les k premiers éléments de la liste sont bien dans l’ordre croissant et on insère le (k + 1)-ième élément de la liste à sa place parmi les éléments déjà triés. Cela peut se faire par échanges successifs avec les éléments précédents : on échange l’élément à insérer avec celui qui le précède tant que l’élément à insérer n’est pas bien placé. La complexité temporelle de l’algorithme du tri par insertion est linéaire dans le meilleur des cas (liste déjà triée) ou quadratique dans le pire des cas (liste triée à l’envers). Sa complexité spatialle est quasi nulle. B 12 Algorithme IPT 2e année 2016-17 Lycée Victor Hugo la liste concaténée Li (triée), x, Ls (triée). La complexité temporelle de l’algorithme de tri rapide est en O(n ln n) dans le meilleur des cas (le pivot sépare la liste en deux sous liste de même taille) ou quadratique dans le pire des cas (les éléments sont tous plus grands ou tous plus petits que le pivot). def tri_fusion ( t ) : n = len ( t ) if n <2: return t else : moitie = n //2 gche = tri_fusion ( t [: moitie ]) droit = tri_fusion ( t [ moitie :]) # fusion des sous - listes tri é es g =0 # indice dans liste gche d =0 # indice dans liste droit trie =[] # liste fusionn é e while g +d < n : if g < moitie and d <n - moitie : if gche [ g ] < droit [ d ]: trie . append ( gche [ g ]) g = g +1 else : trie . append ( droit [ d ]) d = d +1 else : if g < moitie : trie . append ( gche [ g ]) g = g +1 else : trie . append ( droit [ d ]) d = d +1 return trie 3 Le tri quicksort (ou tri rapide) A Le principe B Algorithme def tri_rapide ( t ) : n = len ( t ) if n <2: return t else : x = t [0] # pivot # fabrication sous - listes inf =[] sup =[] for i in range (1 , n ) : y=t[i] if y > x : sup . append ( y ) else : inf . append ( y ) # tri des listes et fusion L_S = tri_rapide ( sup ) L_I = tri_rapide ( inf ) L_I . append ( x ) L_I . extend ( L_S ) return L_I Script 5 H Écrire une fonction de tri rapide qui effectue le tri en place, i.e. avec une complexité spatiale quasi nulle. Soit une liste à trier. Le tri rapide repose de nouveau sur un principe récursif ; on choisit un élément x (dit pivot) dans la liste L à trier, on fabrique la liste Li (sans la trier pour l’instant) des éléments de L inférieurs à x, celle Ls des éléments strictement supérieurs à x. On trie (récursivement) Li et Ls , la liste triée est alors 13