CLASS DE CALCUL MATRICIEL, DIAGONALISATION DE MATRICES SYMÈTRIQUES RÉELLES.

f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 - 17 mai 2006 à 04:35
tybman Messages postés 5 Date d'inscription jeudi 2 octobre 2003 Statut Membre Dernière intervention 31 octobre 2006 - 5 févr. 2007 à 22:31
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/37616-class-de-calcul-matriciel-diagonalisation-de-matrices-symetriques-reelles

tybman Messages postés 5 Date d'inscription jeudi 2 octobre 2003 Statut Membre Dernière intervention 31 octobre 2006
5 févr. 2007 à 22:31
Moi je suis un mathématicien et je trouve cela tout simplement fabuleux
cs_cantador Messages postés 4720 Date d'inscription dimanche 26 février 2006 Statut Modérateur Dernière intervention 31 juillet 2021 13
25 mai 2006 à 11:32
Zéro n'existait pas à l'origine..
c'est pour çà qu'il n'existe pas d'année zéro.
il a donc fallut inventer ce chiffre.
Pour ma part, je concerve cet élément tant que je peux dans les tableaux sinon à chaque fois je tombe sur des cas non prévus déclenchant des exceptions.
JnBiz Messages postés 18 Date d'inscription mardi 18 avril 2006 Statut Membre Dernière intervention 30 mai 2006
17 mai 2006 à 20:59
Brunews, il s'agit d'une convention mathématique, je n'y peux rien, de même le dernier numéro est le numéro n qui correspond également à la taille de la matrice (ce qui n'est pas le cas si on numérote à partir de 0). Une matrice n'est pas qu'un tableau, il y a des règles, des conventions, on ne peut pas faire ce qu'on veut et decréter que le premier élément est placé en (0,0).
C'est juste que les logiques mathématique et informatique différent.
Mais quand il ne s'agit pas de matrices, je rejoins évidemment votre point de vue.
BruNews Messages postés 21040 Date d'inscription jeudi 23 janvier 2003 Statut Modérateur Dernière intervention 21 août 2019
17 mai 2006 à 20:35
Salut f0xi,

à propos du NULL : à moins d'un cpu comprenant la philo, par force une comparaison à null (comme à quoi que ce soit d'autre) ne se fait que par arythmétique, aucune valeur 'floue' représentable en binaire ni autre base. Il est d'usage de définir NULL à zéro.

JnBiz > 1 comme base d'indexation n'est en RIEN logique, ça indique simplement l'incompréhension de ce qu'est un tableau. L'index indique l'offset pour accéder à l'élément partant de l'adresse de base du tableau:
exemple d'un tableau de INT32:
tab[n] se trouve à: adresse(tab) + n * sizeof(INT32)
Le 1er élém est donc bien à: adresse(tab) + 0 * sizeof(INT32)
Ceci ne te parait pas nettement plus logique ???
JnBiz Messages postés 18 Date d'inscription mardi 18 avril 2006 Statut Membre Dernière intervention 30 mai 2006
17 mai 2006 à 20:23
Je me suis fait mal comprendre:
J'ai très bien compris que les tableaux dynamiques commencent par 0, seulement dans l'utilisation pratique d'une matrice (donc par un humain) l'élément (1,1) est par convention mathématique le premier élément, d'où ce décalage.
De même pour l'utilisation des fonctions, methode(A,B,C) C := Methode(A,B) ont beau être équivalent pour le calcul, mais pour l'utilisateur (là encore humain) la 2° solution est plus pratique.
Pour résumer je cherche à faire quelque chose de pratique et de naturel pour l'humain qui ne le soit pas forcément pour l'ordinateur (avoue qu'il est plus facile de réecrire un programme que de réecrire l'algèbre des matrices ;-)).
Par contre la dernière solution add(M1,M2) (additionne M1 et M2 et place le resultat dans M1)) me parait un bon compromis, même si cela n'est pas forcément optimisé pour tout types de calculs (genre M3:=M1+M2). Il ne faut pas oublier que le principal facteur limitant dans le calcul matriciel est l'accès en mémoire qu'il faut limiter au max.
C'est pour ca que j'ai prévu 2 types de calcul:
M1.add(M2) (M1=M1+M2)
et M3:=add(M1,M2) (M3=M1+M2)

Maintenant je me demande si le plus pratique ne serait pas de faire un parser qui gérerait les expression du type "M3=(M1+M2)*M2" cela éviterait d'avoir à gérer des matrices temporaires ce qui est pénible.
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
17 mai 2006 à 19:44
alors, pour un humain oui, la logique veux qu'on appel le premier tiroir d'un meuble le tirroir numero 1.
mais pour un ordinateur cette logique n'est pas applicable le premier tiroir serat le tirroir numero 0.
car si il n'y a pas de tirroir il n'y a pas de valeur donc "null" et null n'est ni 0, ni 1, ni -1 ...
c'est une valeur flou qui determine un element vide, sans valeur.

et quand tu fait un SetLength sur une matrice dynamique, le premier indice serat 0 et non 1.
il faut donc prendre en compte cela car si tu fait SetLength(matrice,10)
low(matrice) renvois 0
high(matrice) renvois 9
et
length(matrice) renvois 10

ce serait comme dire, je compte les minutes a partir de 1 car c'est moins chiant ...
et donc on se retrouve avec des heures comprise dans un interval :
1h 1m 1s 1ms .. 24h 60m 60s 1000ms
ce qui serait une aberation.

si cela te parait plus logique, et je le concois bien, c'est parce que depuis qu'on est tout petit on nous dis que 0 n'est pas une valeur et quand on apprend a compter on compte de 1 a N ... ce qui est par definition mathematiquement inexacte.
on devrait apprendre a compter a partir de Zero qui est un nombre comme les autres et qui surtout est le premier nombre de toute base.
base 2 : 0..1
base 6 : 0..6
base 10: 0..9
base 16: 0..F

donc il est parfaitement logique que le premier indice d'un tableau soit 0 car si le tableau n'a pas de longeur l'indice 0 n'existe pas et on provoqueras une erreur en voulant y acceder.

car par exemple, si nous commencions tout nos tableaux a 1 ...
que ce passerais t'il quand dans un interval -N...+N nous arriverions a zero ?
simple ... il y'aurait un trou qui provoquerais une reaction en chaine ce qui pourrais dechirer le continium espace temps de l'univers entier et brisans ainsi l'equilibre Matiere/Anti-matiere et provoquant le plus grand cataclysme que l'univers est connus, une explosion de plusieurs yotta de yotta de yotta de yotta-tonnes qui detruirait toutes les structures dimensionnelles et qui auraient donc pour effet de boucher les toilettes de notre bon vieux Dieu.

bref ... ça craint du boudin.

donc fait comme tu le sens, mais d'experience je puis t'affirmer qu'il est preferable de compter a partir de 0 ... au moins pour eviter l'apocalypse.

^^


"Donc, si j'ai bien compris, on ne pourra pas traiter les matrices comme des nombres avec des fonctions."

mmm .. si pourquoi ne pourrait-on pas ?
aucune difference entre :

methode(A,B,C)
et
C := Methode(A,B)

c'est meme mieux et plus performant d'utiliser une procedure.
il faut penser "informatique" et pas "mathematique"

si tu connais un peu l'electricité, vois un peu cela comme la difference entre un branchement en serie et un branchement en parallele.

C := methode(A,B) (en serie)
methode(A,B,C) (en parallele)

c'est juste pour faire une analogie.

si A B et C sont des ampoules ... et qu'on les branches en serie C aurat moins de courant et donc brilleras moins.
par contre si on les mets en parrallele ... la elle brillerons toute a la meme intensitée...

ben la c'est pareil.

en passant j'ai fait une erreur : procedure (const M1,M2 : tmatrix; M3: TMatrix)
il ne faut pas mettre VAR.

dans une procedure on fournis 3 variable qui sont des references d'objet.
on vas donc travailler avec ces references ce qui serat plus rapide pour assigner les resultats.

avec une fonction, on doit instancier dynamiquement le resultat pour au final l'assigner a un objet existant. ce qui demande donc des traitements plus (creation > assignation)
sans parler de la gestion de la liberation, au plus propre il faudrait ecrire :

with methode(M1,M2) do
M3.Cells := Cells;
Free;
end;

et encore ... pas sur que ça marche et c'est pas trés propre je trouve.
car c'est l'utilisateur qui doit veiller a la liberation ... (on est pas en C++)

le mieux donc est de travailler avec des procedures et des buffers si besoin.

procedure methode(M1,M2,MR);
var buffer;
begin
Buffer := Create;
Buffer.Cells := M1.Cells (calcul) M2.Cells;
MR.Cells := Buffer.Cells;
Buffer.Free;
end;

ce qui ne nous empeche pas de faire :

methode(M1,M2,M1) ou methode(M1,M2,M2)

ou encore :

add(M1,M2) (additionne M1 et M2 et place le resultat dans M1)
sub(M1,M2) (soustrait M1 et M2 et place le resultat dans M2)
JnBiz Messages postés 18 Date d'inscription mardi 18 avril 2006 Statut Membre Dernière intervention 30 mai 2006
17 mai 2006 à 17:38
Tout d'abord, merci foxi de toutes ces suggestions.
Donc, si j'ai bien compris, on ne pourra pas traiter les matrices comme des nombres avec des fonctions.
Pour ce qui est de la numérotation des indices, je tiens à conserver le fait que le premier élément soit le numéro 1, car c'est quand même beaucoup plus logique pour une matrice.
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
17 mai 2006 à 04:35
pour les fonctions qui renvois des objets :

function Methode(obj1, obj2 : TClasse) : TClasse;

fait plutot cela :

procedure Methode(const Obj1, Obj2 : TClasse;var ObjResultat : TClasse);

et ne fait pas la creation dans la procedure.

il faudrat ensuite faire comme cela :

procedure Methode(const Obj1, Obj2 : TClasse;var ObjResultat : TClasse);
begin
if not assigned(ObjResultat) then exit;
ObjResultat.Prop := Obj1.prop (calcul) Obj2.Prop;
end;

le "not assigned" permet de controller si l'objet est instancié ou non ... auquel cas il ne faut pas provoquer d'erreur de violation donc on sort de la procedure.
on pourrait egalement generer une exception (classe exception) avec cette methode :

type
EEObjectNotAssigned = class(Exception);

procedure Methode(const Obj1, Obj2 : TClasse;var ObjResultat : TClasse);
begin
if not assigned(ObjResultat) then begin
raise EEObjectNotAssigned.Create('Erreur procedure [Methode] objet resultat non créé.');
exit;
end;
ObjResultat.Prop := Obj1.prop (calcul) Obj2.Prop;
end;

et dans le code qui appel la procedure :

begin
try
// on essaye d'appliquer la methode
Methode(M1,M2,MR);
except
// MR ne semble pas etre instancié
on EEObjectNotAssigned do begin
// alors on le crée
MR := TClasse.Create;
// et on retente d'appliquer la methode
Methode(M1,M2,MR);
end;
end;
end;

si on ne gere pas l'exception, alors on aurat un message durant l'execution ... avec la gestion de l'exception en interne, on aurat pas de message durant l'execution.
bien sur, si on est en pleine session Delphi, comme l'execution est en mode de deboggage l'exception s'afficheras.
il faut lancer l'executable independament de Delphi pour ne pas la voir.

les fonctions ne doivent jamais renvoyer une classe ... ceci empeche la liberation propre de ces derniere.
on peu cependant renvoyer des tableaux ou des enregistrements (record) ou encore des ensembles (set of) mais aucunement des classes.

de meme que pour ta fonction "copy" qui devrait s'appelée "Assign" ou "AssignTo" tout depend le sens d'assignation qu'on desire et devrais appartenir a la classe TMatrix :

procedure TMatrix.AssignFrom(const Matrix : Tmatrix);
var i,j:integer;
begin
if Not Assigned(Matrix) then exit;
with Self do begin
ColCount := Matrix.ColCount;
RowCount := Matrix.RowCount;
FSquare := Matrix.Square;
FSym := Matrix.Sym;
FLo := Matrix.Lo;
FUp := Matrix.Up;
FDiag := Matrix.Diag;
FHess := Matrix.Hess;
FTriDiag := Matrix.TriDiag;
// on peut faire ça avec les tableaux a partir du moment qu'ils sont du meme type!
Cells := Matrix.Cells;
end;
end;

ensuite on regle les petits problemes :

function TMatrix.GetRowCount : integer;
begin
// on renvois la longueur et non l'indice maximum
// un tableau dynamique commence a 0
result := length(Cells);
end;

procedure TMatrix.SetRowCount(const RC : integer);
begin
// si je veux 10 lignes elle seront numerotée de 0 a 9
SetLength(Cells, RC);
end;

function TMatrix.GetColCount : integer;
begin
// idem on commence a 0 et on renvois la longueur
result := length(Cells[0]);
end;

procedure TMatrix.SetColCount(const CC : integer);
begin
// tu le savais pas ça ? ben maintenant si ...
SetLength(Cells,length(Cells),CC);
end;

encore d'autre petit probleme :

procedure TMatrix.Define;
var i,j,n : integer;
a,b : extended;
begin
n := RowCount;
if n = ColCount then begin
FSquare := True; Fsym := True; FLo := True;
FUp := True; FDiag := True; FHess := True;
FTridiag := True;
for i := 0 to n-1 do begin
for j := 0 to n-1 do begin
a := Cells[i,j];
b := Cells[j,i];
FSym := not (a <> b);
FDiag := not (a <> 0);
if i<j then begin
FLo := FDiag;
FTriDiag := not ((j > i+1) and FDiag);
end;
if i>j then begin
FUp := FDiag;
FTriDiag := not ((i > j+1) and FDiag);
FHess := FTriDiag;
end;
end;
end;
end else begin
FSquare := False; Fsym := False; FLo := False;
FUp := False; FDiag := False; FHess := False;
FTridiag := False;
end;
end;

procedure TMatrix.Transpose;
var i,j,RC,CC:integer;
M1 : TMatrix;
begin
RC := RowCount;
CC := ColCount;
M1 := TMatrix.Create(RC,CC);
M1.AssignFrom(Self);
RowCount := CC;
ColCount := RC;
for i := 0 to RowCount-1 do
for j := 0 to ColCount-1 do
Cells[i,j] := M1.Cells[j,i];
M1.Free;
end;

il faut bien rédadapter avec les nouveautées et les corrections d'erreurs.


procedure TMatrix.Add(M:TMatrix);
var i,j,RC,CC:integer;
begin
RC := M.RowCount;
CC := M.ColCount;
if (RC<>RowCount) and (CC<>ColCount) then exit;
for i := 0 to RC-1 do
for j := 0 to CC-1 do
Cells[i,j] := Cells[i,j] + M.Cells[i,j];
end;

procedure TMatrix.Sub(M:TMatrix);
var i,j,RC,CC:integer;
begin
RC := M.RowCount;
CC := M.ColCount;
if (RC<>RowCount) and (CC<>ColCount) then exit;
for i := 0 to RC-1 do
for j := 0 to CC-1 do
Cells[i,j] := Cells[i,j] - M.Cells[i,j];
end;


et enfin, pour liberer un objet on appel pas directement Destroy mais Free!
Free fait des verifications avant d'appeler Destroy.

et si on veux pas se prendre la tete a savoir ou commence le tableau et ou il finit :

for i := low(Cells) to high(Cells) do
for j := low(Cells[i]) to high(Cells[i]) do
...

voila je pense t'avoir donner assé d'elements pour que tu puisse t'en sortir.

a plus
Rejoignez-nous