|
Publicité ' | |||||||||||||||||||||||
|
|
#1 | ||||||||||
|
Membre éclairé
![]() Florian Inscription : mai 2002 Messages : 372 ![]() |
Salut,
Pour ma première contribution (c++), je vous propose une régression circulaire sur un ensemble de points (x; y). Voilà la déclaration de la classe Matrix (matrix.h) : Code c++ :
Code c++ :
Code c++ :
Code c++ :
g++ main.cpp matrix.cpp -o test La sortie est du style : Code :
http://floriansella.free.fr/index.ph...y=ti&billid=92 Histoire de pas faire un truc creux, la régression d'un ensemble de points par un cercle (xc; yc; R) fonctionne comme la régression d'un ensemble de points par une droite. C'est une régression au sens des moindres carrés et on parvient à un système matriciel qu'il suffit de résoudre par la méthode basique de Gauss-Jordan (pivot de Gauss). Flo. |
||||||||||
|
00
|
|
|
#2 |
|
Membre Expert
![]() Chercheur Inscription : mars 2010 Messages : 1 143 ![]() |
Bonjour,
merci beaucoup pour cette implémentation. Voici quelques améliorations possibles : 1. ne pas inverser explicitement A pour calculer y=inv(A)*x mais résoudre directement Ax=y par élimination de Gauss; 2. faire en sorte que Matrix soit bien une matrice : en lisant rapidement, je n'ai pas vu de stockage des coefficients; 3. encapsuler tes données; 4. passer par class plutôt que par struct; 5. ne pas mélanger codes d'erreur et exceptions; faire soit l'un soit l'autre. 6. paramétrer avec des templates pour laisser à l'utilisateur le choix de la précision des calculs et éventuellement du type de données; 7. éviter de faire du static quand ce n'est pas la peine. Bonne continuation |
|
|
00
|
|
|
#3 |
|
Membre éclairé
![]() Florian Inscription : mai 2002 Messages : 372 ![]() |
Merci pour les commentaires, Aleph69.
Ce code est plutôt destiné à mon éducation (et celle de ceux que mon code intéresse) ... j'ai pas pour objectif de le proposer chez boost (ils doivent déjà avoir des trucs pour faire ça je suppose) Ceci étant dit, je vais corriger le point 5 qui est effectivement maladroit même pour un code de ce type, et proposer une autre version en considérant votre point 1, qui m'intéresse évidemment. Toutefois, si je n'ai rien à redire à vos remarques, qui sont sûrement pertinentes, je laisserai à tout un chacun le choix de l'implémentation d'une version propre à ses besoins, si tant est que quelques uns s'entichent de ces quelques lignes de codes. Flo. |
|
00
|
|
|
#4 |
|
Membre éclairé
![]() Florian Inscription : mai 2002 Messages : 372 ![]() |
J'ai corrigé les points 1 et 5 décrits par Aleph69 en lieu et place du code initial dans mon premier message.
Encore merci Aleph69. Flo. |
|
00
|
|
|
#5 |
|
Membre Expert
![]() Chercheur Inscription : mars 2010 Messages : 1 143 ![]() |
J'ai encore une remarque, j'espère que ça ne pose de problème. Pour s'assurer qu'on ne provoque pas un overflow, on ne compare pas le pivot avec epsilon. La fonction dlamch de la bibliothèque lapack calcule un nombre safemin correspondant au plus petit nombre par lequel on peut diviser sans provoquer d'overflow. Je pense que c'est à cette quantité que tu devrais comparer les pivots. Moralement, il faut que c=b/a ne dépasse le plus grand nombre flottant représentable en machine MAX ( il est donné par std::numeric_limits<T>::max() en C++ ). On doit donc avoir |b/a|=|b|/|a|<=MAX, soit encore |b| <= MAX*|a|. Je pense que cela revient à passer par dlamch mais à vérifier quand même, il y a peut-être une subtilité. En tous les cas, la routine dlamch est ce qui se fait de plus robuste dans le domaine. En testant avec epsilon, tu arrêtes parfois prématurément l'algorithme alors que la matrice était bien factorisable à la précision machine.
EDIT : ajout du lien vers la routine dlamch. EDIT2 : encore une chose, en C++, on préfère inclure cmath plutôt que math.h |
|
|
00
|
|
|
#6 | ||||||
|
Membre éclairé
![]() Florian Inscription : mai 2002 Messages : 372 ![]() |
Salut Aleph69,
J'ai remplacé l'include de math.h pour celui de cmath. Quant à ton histoire de l'epsilon, utiliser LAPACK ça ne m'intéresse pas. En revanche, je veux bien essayer de comprendre ce que tu me racontes et de l'intégrer dans mon code. J'ai donc fait une fonction qui teste un pivot sur une ligne et renvoie true si le résultat de la multiplication du dénominateur (de chaque division) par std::numeric_limits<double>::max() est inférieur au pivot, false sinon. Code c++ :
J'ai fait un gauss-jordan sur le système AX=B suivant (sur ma machine std::numeric_limits<double>::epsilon() vaut 2.22045e-016) : Code :
Code :
Faut dire qu'avec INSPECT_PIVOT à 1, les valeurs du système en cours de résolution montent à des + ou - 1e+17 et les opérations suivantes doivent en pâtir. Donc je sais pas si j'ai bien tout compris. Flo. |
||||||
|
00
|
|
|
#7 | ||
|
Membre Expert
![]() Chercheur Inscription : mars 2010 Messages : 1 143 ![]() |
Bonjour,
il y a plusieurs phénomènes en jeu dans ce que tu décris. Une première question est de savoir si une matrice est factorisable à la précision machine, c'est-à-dire si le processus d'élimination gaussienne peut être mené à son terme sans division par zéro, sans overflow et sans underflow, toute considération sur la stabilité numérique de l'algorithme mis à part. C'est une question d'informatique pur : l'algorithme arrive-t-il à son terme? Ainsi, lorsque tu divises par un pivot, il faut que cette division soit bien représentable en machine. Je te faisais juste remarquer que tu pouvais diviser par un nombre inférieur à epsilon sans nécessairement provoquer d'overflow. Pour tester cela, je t'ai indiqué la fonction dlamch, non pas pour l'utiliser mais pour regarder ce qui y est fait. Il y est défini un nombre sfmin correspondant au plus petit nombre par lequel on peut diviser un autre nombre sans provoquer d'overflow. Ce nombre est défini de la manière suivante : Code :
La question de la stabilité numérique de l'algorithme est une autre chose. Quelle que soit le choix de ton pivot, l'erreur relative sur la solution sera au mieux bornée supérieurement par un terme supérieur à 2^(n-1) où n désigne la taille de la matrice. On connaît d'ailleurs les matrices pour lesquelles cette borne est atteinte. De ce fait, trouver une solution plus précise avec telle ou telle méthode de pivot ne la rend pas meilleure : on saura toujours trouver des matrices pour lesquelles l'erreur sur la solution explose. A noter que l'approche standard appelée "pivotage partiel par ligne" consiste à choisir pour pivot celui dont la valeur absolue est maximale. Le test de quasi-singularité n'intervient qu'après pour tester si cette valeur maximale est bien supérieure à sfmin. Par ailleurs, si tu veux améliorer la stabilité de ton algorithme, il faut pré-traiter ton système linéaire à l'aide d'un préconditionneur. Une méthode simple consiste par exemple à diviser chaque ligne par sa norme. Sa variante par colonne est également très utilisée. Tu peux également raffiner itérativement la solution calculée en mélangeant plusieurs degrés de précision (float/double ou double/long double); ceci te permettra également de fournir des estimations des erreurs de calcul à tes utilisateurs. Enfin, si le sujet t'intéresse, tu peux tenter de percer les derniers mystères de la méthode de Gauss. |
||
|
|
00
|
Copyright © 2000-2013 - www.developpez.com