Al g ori thmi q u e N u méri q u e

Dimension: px
Commencer à balayer dès la page:

Download "Al g ori thmi q u e N u méri q u e"

Transcription

1 Al g ori thmi q u e N u méri q u e Rapport de TP "Gauss et compagnie" Ce rapport présente deux algorithmes de produit matriciel (méthode "Classique" et de Strassen) ainsi que deux algorithmes de résolution de systèmes linéaires. Le pivot de Gauss pour triangulariser la matrice et la méthode de Gauss-Jordan qui diagonalise. La dernière partie porte sur le calcul du déterminant d'une matrice avec deux algorithmes : celui de Jordan-Bareiss qui concerne le cas particulier des matrices à cofficients entiers et celui du pivot de Gauss. 1. Le produit matriciel a. Méthode classique La méthode la plus simple pour calculer un produit matriciel est de trouver les coefficients un à un avec la formule ci-contre. implémentation python avec numpy Exemples >>> E1 = matrix( ( 2) ) >>> E2 = matrix( ( 3) ) >>> calc_num. prod_mat_classique( E1 E2) array( [ [ 6. ] ] ) >>> E1 = matrix( ( ) ). reshape( 3 3) >>> E2 = matrix( ( ) ). reshape( 3 3) >>> calc_num. prod_mat_classique( E1 E2) array( [ [ ] [ ] [ ] ] ) 1 /8

2 b. L'algorithme de Strassen L'algorithme de Strassen permet de calculer un produit matriciel en effectuant moins de multiplications car la méthode "classique" n'est pas optimale. Cet algorithme ne s'applique que sur les matrices dont la taille est une puissance de 2. Ce n'est pas vraiment une limitation car n'importe quelle matrice peut devenir de cette forme en completant les lignes et les colonnes par des L'algorithme de Strassen est récursif : à chaque étape la matrice est divisée en quatres sous-matrices l'amélioration consistant à effectuer des opérations plus simples entre celles-ci par rapport à la méthode dite classique. Le cas d'arrêt de la récusivité est celui où les matrices sont de taille 1x1. Comparatif entre les opérations des 2 méthodes la méthode de Strassen n'utilise que 7 multiplications mais bien plus d'additions. méthode de Strassen méthode classique récursive

3 3 /8

4 Comparaison des méthodes classique et Strassen La complexité du produit classique est grande en O(n3). L'algorithme de Strassen en économisant une multiplication coûteuse permet de réduire cette complexité à O(n28) au prix d'un plus grand nombre d'opérations d'additions. La différence ne doit apparaitre que sur de grandes matrices. Le temps d'exécution a été obtenu avec le module python timeit et la commande suivante dans une console : pour la méthode classique python - m timeit " import random; from numpy import *; import calc_num; a = random. random( ( 4 4) ) ; b = random. random( ( 4 4) ) ; calc_num. prod_mat_classique( a b) " pour la méthode de Strassen python - m timeit " import random; from numpy import *; import calc_num; a = random. random( ( 4 4) ) ; b = random. random( ( 4 4) ) ; calc_num. prod_mat_strassen( a b) " pour la fonction intégrée de numpy (dot) python - m timeit " import random; from numpy import *; a = random. random( ( 4 4) ) ; b = random. random( ( 4 4) ) ; dot( a b) " Remarque : Les tests ont été fait sur un ordinateur équipé d'un Athlon XP à 13 GHz. matrice 4x4 classique 758 usec matrice 8x8 327 msec matrice 32x msec matrice 16x16 matrice 64x64 matrice 512x512 Strassen 289 msec 19 msec 22 msec 131 msec 143 sec 613 sec 880 msec dot (numpy) 349 usec 362 usec 415 usec 622 usec 19 msec 238 sec Contrairement à ce qui avait été imaginé l'algorithme de Strassen ne prend pas le dessus sur la méthode classique. Plusieurs explications sont possibles : La récursivité rend l'algorithme de Strassen moins performant par rapport à celui de la méthode classique. L'implémentation dans un langage interprété comme python ralentit les 2 algorithmes et repousse le moment où l'algorithme de Strassen prend l'avantage. On remarque que la fonction interne de numpy est clairement plus performante elle est probablement compilée. Une version en C de l'algorithme de Strassen a été implementée pour tester avec des matrices plus grandes mais le résultat était le même. 4/8

5 2. Résolution de systèmes linéaires a. Méthode du pivot de Gauss La méthode du pivot de Gauss propose de résoudre un système d'équations en triangonalisant la matrice contenant les inconnues des équations. La dernière équation devient une simple égalité et on remonte chaque ligne de la matrice en trouvant le résultat d'une nouvelle inconnue. Exemple avec le système d'équation : La matrice correspondante est la suivante : >>> M matrix( [ [ ] [ ] [ ] ] ) >>> pivot_gauss( M) matrix( [ [ ] [ ] [ ] ] ) Une fois la matrice triangonalisée la solution est rapide à trouver : Remarque : L'algorithme du pivot de Gauss ci-dessus ne gère pas le cas où le pivot serait égal à 0 (ce qui conduirai à une divison par zéro). La solution est de chercher un pivot non nul en échangeant les lignes de la matrice. Cette solution a été implementée dans la méthode suivante et aurai pu l'être aussi ici. b. Méthode Gauss-Jordan La méthode Gauss-Jordan est une variante du pivot de Gauss plus pratique en algorithmie. La matrice des équations est diagonalisée au lieu d'être triangonalisée la solution du système d'équation est alors immédiate. Sur l'exemple précedent : >>> E5 matrix( [ [ ] [ ] [ ] ] ) >>> gauss_j ordan( E5) matrix( [ [ ] [ ] [ 1. 5] ] ) 5 /8

6 Le problème des très petites valeurs Lorsque l'équation contient une très petite valeur il faut éviter de l'utiliser comme pivot. Dans l'exemple ci-contre le coefficent de x est très petit par rapport aux autres valeurs. >>> E6 matrix( [ [ e e e+00] [ e e e+00] ] ) >>> gauss_j ordan( E6) matrix( [ [ ] [ ] ] ) >>> set_printoptions( precision=12 suppress=false) >>> gauss_j ordan( E6) matrix( [ [ ] [ ] ] ) Résolution "à la main" : Le calcul de x se fait avec des nombres très petits et très grands : >>> ( 2-10e12) /( 1-10e12) La différence entre 2 et 1 se trouve "poussée" très loin après la virgule et lorsque l'on multiplie... >>> ( 2-10e12) /( 1-10e12) *10e Il y a perte d'une précision significative >>> 10e12 - ( 2-10e12) /( 1-10e12) *10e ( la fraction a été " simplifiée" ) Le résultat final est moins précis. Afin de minimiser c'est perte de précision il faut éviter de prendre des pivots très petits. 6/8

7 Algorithme modifié Dans cet algorithme le plus grand pivot est selectionné par permutation. Sur l'exemple précédent la nouvelle méthode de selection du pivot change le résultat : >>> E6 matrix( [ [ e- 11 [ e+00 >>> gauss_j ordan( E6) matrix( [ [ 1. [ e e+00 La valeur de x a changé e+00] e+00] ] ) ] ] ] ) L'effet papillon De petites variations sur les coefficients du système d'équations peuvent provoquer de grandes différences sur la résolution. Dans l'exemple qui suit le vecteur résultat change très peu et pourtant la résolution est différente. >>> E7 matrix( [ [ 1 7. [ [ [ >>> gauss_j ordan( E7) matrix( [ [ 1. [ 1. [ [ ] 23. ] 33. ] 31. ] ] ) 09] 2. 55] 55] 1. 27] ] ) >>> E8 matrix( [ [ 1 7. [ [ [ >>> gauss_j ordan( E8) matrix( [ [ 1. [ 1. [ [ ] 22. 9] 33. 1] 3 9] ] ) 1. 84] 1. 62] 32] 1. 41] ] ) 7 /8

8 3. Calcul de déterminants a. En utilisant le pivot de Gauss Dans le cas d'une matrice triangulaire le calcul du determinant se simplifie en étant égal au produit des coefficients diagonaux. L'utilisation de l'algorithme du pivot de Gauss précédent permet d'aboutir facilement à un algorithme de calcul du déterminant. Exemple : >>> E10 matrix( [ [ ] [ ] [ ] ] ) >>> det_gauss( E10) Calcul du déterminant avec la fonction intégrée de numpy : >>> from numpy. linalg import * >>> det( E10) b. Algorithme de Jordan-Bareiss Dans le cas particulier des matrices à coefficients entiers l'algorithme de Jordan-Bareiss permet de calculer le déterminant sans passer par les flottants. Cet algorithme permet d'obtenir un résultat plus précis que la méthode précédente du pivot de Gauss. Exemple : >>> E11 matrix( [ [ 1 2 3] [ 4 5 6] [ 7 8 1] ] ) >>> from numpy. linalg import * >>> det( E11) Cet exemple ne montre pas l'amélioration car les erreurs d'arrondis de la méthode du pivot de Gauss ont joués dans le "bon sens". La seule amélioration est que le résultat est de type entier (le déterminant d'une matrice à coefficents entiers est entier). >>> det_bareiss( E11) 24 8/8