// Unit MATRICE // OBJECT : matrix and vector aritmetic (inversion, addition, multiplication ...) // first written in Turbo-Pascal (02-05-1991) // adapted to Delphi 4 (28-05-1999) // NOTES // Note 1 : these routines operate on dynamical square matrix and dynamical vectors // (RCOMat and RCOVec),whose dimension should have been be declared with // the InitMat(Dim) procedure (error if if not initialize). // Note 2 : the index (i,j..) are "zero based", this is a constraint of // Delphi4 dynamical matrix structure. // Note 3 : the "if true ..." instructions are remainings of the TurboPascal 5 version, // I should have transform them into "try .. except .." instructions in order to // manage memory overflow errors. If somebody do that, I can mail me the upgraded version. // Note 4 : I checked this unit with the small program at the end of that file. // The time required to invert a 200 x 200 extented matrix is of about 2 seconds, // (AMD K6-2 overclocked at 400 MHz), and the accuracy compatible with extended // accuracy (10E-17 .. 10E-18) // if you find that unit usefull, please let me know : // remi CORMIER, rco@club-internet.fr UNIT Matrice; //determinant为行列式的值 INTERFACE uses SysUtils,Dialogs,PerlRegEx,classes; // I would'nt worry if you change "RCOMat" to "YourNameMat" ... TYPE RCOMat = array of array of extended; RCOVec = array of extended; VAR ErreurMat : integer; { code d'erreur, Cf ci apr鑣 : } CONST MatCorrect = 0; MatNonInit = 1; MatDebordTas = 2; MatDetNul = 3; //两个行列相同的矩阵相加 function MatAddMat(A,B:RCOMat;i,j:integer):RCOMat; //将矩阵转成相反数,求A的负 function FuMat(A:RCOMat;i,j:integer):RCOMat; //小数转分数 function Fraction(decimal: double): string; //从文本文件中读取矩阵到A ,i,j为返回的矩阵长度 procedure MatFromText(var A:RCOMat;path:string;var i,j:integer); //用MessageBox显示出矩阵,isT表示是否用分数显示,默认为用小数显示 procedure MatToMessageBox(A:RCOMat;i,j:integer;isT:boolean=false;isToText:boolean=true); // Returns (W) = (U)-(V) ; U,V,W : Vectors PROCEDURE DifVec(U,V:RCOVec;var W:RCOVec); // Returns the current dimension of matrix and vectors FUNCTION DimMatrices:integer; // Initialize the matrix and vectors dimension with Dim PROCEDURE InitMat(Dim:integer); // Returns [B], the invert matrix of [A] ([A]x[B]=[I]), Det is the determinant of [A] (and [B]) //求A的逆阵B 只有方阵才有逆 返回det为A的行列式的值 PROCEDURE InvMat(A:RCOMat;var B:RCOMat;var Det:extended{MatElem}); // Returns [C] = [A]x[B] PROCEDURE MatMat(A,B:RCOMat;var C:RCOMat); // Returns [C] = [A]x[B] ; same as MatMat, except that [A] and [B] can be // rectangular. [A] have Dim rows and M columns, [B] have M rows and P colums : // [A]N,M x [B]M,P -> [C]N,P (with N = DimMat) PROCEDURE MatMatR(A,B:RCOMat;M,P:integer;var C:RCOMat); // Returns the unit matrix (one on diagonal, zero everywhere else)返回单元矩阵 PROCEDURE MatUnt(var MU:RCOMat); // Returns the (W)=[M]x(V) ; [M] RCOMat, (V) and (W) RCOVec PROCEDURE MatVec(M:RCOMat;V:RCOVec;var W:RCOVec); // Returns the norm of vector (U) FUNCTION Norme(U:RCOVec) : extended; // Returns the norm of [A], [A] can be rectangular, with n row and m columns FUNCTION NorMat(A:RCOMat;n,m:integer):extended; // Returns the scalar product of the vector (U) and (V) FUNCTION PrdScal(U,V:RCOVec) : extended; // Multiply the matrix [A] with x, the result in matrix [B] PROCEDURE ScalMat(x:extended;A:RCOMat;var B:RCOMat); // Multiply the vector (U) with x, the result in vector (V) PROCEDURE ScalVec(x:extended;U:RCOVec;var V:RCOVec); // Addition of two square matrix [A] and [B], result in [C] PROCEDURE SomMat(A,B:RCOMat;var C:RCOMat); // Addition of two vectors, (U) and (V), result in (W) PROCEDURE SomVec(U,V:RCOVec;var W:RCOVec); // Solution of the linear system [A]x(X)=(B) // vector (X) is the solution // check Det for valid solution (should be <> 0) PROCEDURE SysLin(A:RCOMat;var X:RCOVec;B:RCOVec;var Det:extended{MatElem}); // Returns the transposed matrix of [A] into [B] (Bij = Aji) 转置行列式 PROCEDURE TransMat(A:RCOMat;var B:RCOMat;n,m:integer); //n,m为A的行、列数 // Returns the text (in french) associated with the error # no FUNCTION MsgErreurMat(no:integer):string; IMPLEMENTATION VAR Ini : boolean; DimMat : integer; TYPE RCoVecE = array of integer; function MatAddMat(A,B:RCOMat;i,j:integer):RCOMat; var C:RCOMat; m,n:integer; begin InitMat(i); SetLength(C,i,j); for m := 0 to i - 1 do for n := 0 to j - 1 do C[m,n] := A[m,n]+B[m,n]; result := C; end; function FuMat(A:RCOMat;i,j:integer):RCOMat; var B:RCOMat; temp,temp1:integer; begin setlength(B,i,j); for temp := 0 to i - 1 do for temp1 := 0 to j - 1 do B[temp,temp1] := -A[temp,temp1]; result := B; end; function Fraction(decimal: double): string; var intNumerator, intDenominator, intNegative: integer; // 声明整数变量为长整数 dblFraction, dblDecimal, dblAccuracy, dblinteger: Double; // 声明浮点数为双精度 temp:string; begin dblDecimal := decimal; //取得目标小数 if trunc(decimal) = decimal then // 如果是整数,则直转 result := floattostr(decimal) else begin if abs(decimal) > 1 then //如果小数大于1 如 10.24 ,进行拆解 begin dblinteger := trunc(decimal); //取出整数部分 dblDecimal := abs(frac(decimal)); //取出小数部分 end else dblDecimal := decimal; dblAccuracy := 0.01; //设置精度 intNumerator := 0; //初始分子为0 intDenominator := 1; //初始分母为1 intNegative := 1; //符号标记为正 if dblDecimal < 0 then intNegative := -1; //如果目标为负,设置负标志位 dblFraction := 0; //设置分数值为 0/1 while Abs(dblFraction - dblDecimal) > dblAccuracy do //如果当前没有达到精度要求就继续循环 begin if Abs(dblFraction) > Abs(dblDecimal) then //如果我们的分数大于目标 intDenominator := intDenominator + 1 //增加分母 else //否则 intNumerator := intNumerator + intNegative; //增加分子 dblFraction := intNumerator / intDenominator; //计算新的分数 end; // edit2.Text := inttostr(intNumerator) + '/' + inttostr(intDenominator); if abs(decimal) > 1 then //如果小数大于1 如 10.24 ,进行拆解 //结果:dblinteger为整数部分, intNumerator为分子,intDenominator为分母 begin if dblinteger<0 then begin dblinteger := -dblinteger; temp := floattostr(dblinteger*intDenominator+intNumerator) + '/' + inttostr(intDenominator); //result := floattostr(dblinteger) + '又' + inttostr(intNumerator) + '/' + inttostr(intDenominator) result := '-'+temp; end else begin temp := floattostr(dblinteger*intDenominator+intNumerator) + '/' + inttostr(intDenominator); result :=temp; end; end else result := inttostr(intNumerator) + '/' + inttostr(intDenominator); end; end; procedure MatFromText(var A:RCOMat;path:string;var i,j:integer); var f:TextFile; n,m:integer; Reg:TPerlRegEx; List:TStrings; s:string; begin i:=0; j:=0; Reg:=TPerlRegEx.Create(nil); List:=TStringList.Create; n:=0; assignFile(f,path); reset(f); while not eof(f) do begin readln(f,s); if not (trim(s)='') then //空行情况的处理 i:=i+1; end; closefile(f); reset(f); s:=''; while not eof(f) do begin Readln(f,s); s:=trim(s); if not (s='') then //空行情况的处理 begin reg.Subject := s; reg.RegEx := ' *'; //以空格为分隔符(空格个数不限) reg.Split(list,MaxInt); j:=list.Count; SetLength(A,i,j); for m := 0 to j - 1 do begin A[n,m]:=strtofloat(list[m]); end; n:=n+1; end; list.Clear; end; list.Free; closefile(f); end; procedure MatToMessageBox(A:RCOMat;i,j:integer;isT:boolean=false;isToText:boolean=true); var m,n:integer; str:string; f:textfile; begin str:=''; for m := 0 to i - 1 do begin str:=str+#13; for n := 0 to j - 1 do begin if isT then str := str+Fraction(A[m,n])+' ' else str:=str+format('%.4f',[A[m,n]])+' '; end; end; showmessage(str); str :=''; if isToText then //将结果保存到文本文件 begin assignfile(f,'.\data\result.txt'); rewrite(f); //清空文本 closefile(f); assignfile(f,'.\data\result.txt'); Append(f); for m := 0 to i - 1 do for n := 0 to j - 1 do begin str:=str+format('%.4f',[A[m,n]])+' '; if n=j-1 then begin writeln(f,str); str := ''; end; end; closefile(f); end; end; FUNCTION MsgErreurMat(no:integer):string; BEGIN MsgErreurMat:=''; case no of MatCorrect : Result:='succ鑣'; MatNonInit : Result:='unit?non initialis閑'; MatDebordTas : Result:='pas assez de m閙oire'; MatDetNul : Result:='d閠erminant nul'; end; END; FUNCTION NorMat(A:RCOMat;n,m:integer):extended; VAR Nor : extended; i,j : integer; BEGIN Nor:=1; for i:=0 to pred(n) do for j:=0 to pred(m) do Nor:=Nor*sqr(A[i,j]); Nor:=sqrt(Nor/n/m); Result:=Nor; END; FUNCTION DimMatrices:integer; BEGIN DimMatrices:=DimMat; END; PROCEDURE Init; begin if not Ini then begin ErreurMat:=MatDebordTas; write('Unit?MATRICES non initialis閑');// I think that write generate the write halt(1); // instruction would generate an exception end; ErreurMat:=0; end; PROCEDURE InitMat(Dim:integer); begin DimMat:=Dim; Ini:=true; Init; end; PROCEDURE MatMat(A,B:RCOMat;var C:RCOMat); var i, j , k : integer; Cij : extended; begin Init; if true then begin for i:=0 to pred(DimMat) do for j:=0 to pred(DimMat) do begin Cij:=0; for k:=0 to pred(DimMat) do Cij:=Cij+A[i,k]*B[k,j]; C[i,j]:=Cij; end; ErreurMat:=0; end else ErreurMat:=MatDebordTas; end; PROCEDURE SomMat(A,B:RCOMat;var C:RCOMat); var i,j : integer; BEGIN if true then begin for i:=0 to pred(DimMat) do for j:=0 to pred(DimMat) do C[i,j]:=A[i,j]+B[i,j]; ErreurMat:=0; end else ErreurMat:=MatDebordTas; END; PROCEDURE MatMatR(A,B:RCOMat;M,P:integer;var C:RCOMat); var i,j,k : integer; Cij : extended; BEGIN if true then begin for i:=0 to pred(DimMat) do for j:=0 to pred(P) do begin Cij:=0; for k:=0 to pred(M) do Cij:=Cij+A[i,k]*B[k,j]; C[i,j]:=Cij; end; ErreurMat:=0; end else ErreurMat:=MatDebordTas; END; PROCEDURE TransMat(A:RCOMat;var B:RCOMat;n,m:integer); //n,m为A的行、列数 var i, j : integer; begin Init; if true then begin for i:=0 to pred(m) do for j:=0 to pred(n) do B[i,j]:=A[j,i]; ErreurMat:=0; end else ErreurMat:=MatDebordTas; end; PROCEDURE MatUnt(var MU:RCOMat); var i,j : integer; begin Init; for i:=0 to pred(DimMat) do for j:=0 to pred(DimMat) do if i=j then MU[i,j]:=1 else MU[i,j]:=0; end; PROCEDURE MatVec(M:RCOMat;V:RCOVec;var W:RCOVec); var i, j : integer; Wi : extended; begin Init; if true then begin for i:=0 to pred(DimMat) do begin Wi:=0; for j:=0 to pred(DimMat) do Wi:=Wi+M[i,j]*V[j]; W[i]:=Wi; end; ErreurMat:=0; end else ErreurMat:=MatDebordTas; end; FUNCTION PrdScal(U,V:RCOVec) : extended; var i : integer; s : extended; begin Init; s:=0; for i:=0 to pred(DimMat) do s:=s+U[i]*V[i]; Result:=s; end; PROCEDURE ScalVec(x:extended;U:RCOVec;var V:RCOVec); var i : integer; begin Init; for i:=0 to pred(DimMat) do V[i]:=x*U[i]; end; PROCEDURE ScalMat(x:extended;A:RCOMat;var B:RCOMat); var i,j : integer; BEGIN for i:=0 to pred(DimMat) do for j:=0 to pred(DimMat) do B[i,j]:=x*A[i,j]; END; PROCEDURE AddVec (Signe:integer;U,V:RCOVec;var W:RCOVec); var i : integer; begin Init; for i:=0 to pred(DimMat) do W[i]:=U[i]+Signe*V[i]; end; PROCEDURE SomVec(U,V:RCOVec;var W:RCOVec); begin AddVec(1,U,V,W); end; PROCEDURE DifVec(U,V:RCOVec;var W:RCOVec); begin AddVec(-1,U,V,W); end; FUNCTION Norme(U:RCOVec) : extended; var i : integer; n : extended; begin Init; n:=0; for i:=0 to pred(DimMat) do n:=n+sqr(U[i]); Result:=sqrt(n); end; PROCEDURE TrianguleO(A:RCOMat;var AT:RCOMat;var Jx,Iy:RCOVecE;var Det:extended); var i, k : integer; im , jm : integer; ATkk : extended; C : extended; Max : extended; SignDet : integer; PROCEDURE InitJxIy; var i:integer; begin for i:=0 to pred(DimMat) do begin Jx[i]:=i; Iy[i]:=i; end; end; PROCEDURE CopiAAT; var i,j:integer; begin for i:=0 to pred(DimMat) do for j:=0 to pred(DimMat) do AT[i,j]:=A[i,j]; end; PROCEDURE Reduit; var i,j:integer; begin ATkk:=AT[k,k]; for i:=k+1 to pred(DimMat) do begin C :=AT[i,k]/ATkk; for j:=k+1 to pred(DimMat) do AT[i,j]:=AT[i,j]-C*AT[k,j]; for j:=0 to k do if j<>k then AT[i,j]:=AT[i,j]-C*AT[k,j] else AT[i,j]:=-C; end; end; PROCEDURE ChercheMax(var Max:extended;var im,jm:integer); var i,j : integer; M : extended; begin Max:=abs(AT[k,k]); im:=k;jm:=k; for i:=k to pred(DimMat) do for j:=k to pred(DimMat) do begin M:=abs(AT[i,j]); if M>Max then begin Max:=M; im:=i;jm:=j; end; end; end; PROCEDURE PermuteCol; var Ind : integer; i : integer; x : extended; begin for i:=0 to pred(DimMat) do begin x:=AT[i,k]; AT[i,k]:=AT[i,jm]; AT[i,jm]:=x; end; Ind :=Jx[k]; Jx[k] :=Jx[jm]; Jx[jm]:=Ind; SignDet:=-SignDet; end; PROCEDURE PermuteLig; var Ind : integer; j : integer; x : extended; begin for j:=0 to pred(DimMat) do begin x:=AT[k,j]; AT[k,j]:=AT[im,j]; AT[im,j]:=x; end; Ind:=Iy[k]; Iy[k]:=Iy[im]; Iy[im]:=ind; SignDet:=-SignDet; end; begin InitJxIy; CopiAAT; k:=0; SignDet:=1; while (k<=pred(DimMat)-1) and (ErreurMat=0) do begin ChercheMax(Max,im,jm); if Max<>0 then begin if k<>jm then PermuteCol; if k<>im then PermuteLig; Reduit; end else ErreurMat:=MatDetNul; k:=k+1; end; Det:=SignDet; for i:=0 to pred(DimMat) do Det:=Det*AT[i,i]; if Det=0 then ErreurMat:=MatDetNul; end; PROCEDURE ResouTrO(A:RCOMat;var X,Y:RCOVec;Jx,Iy:RCOVecE); var i, j : integer; PVX : RCOVec; Xi : extended; begin if true then begin SetLength(PVX,DimMat); for i:=pred(DimMat) downto 0 do begin Xi:=Y[Iy[i]]; for j:=0 to i-1 do Xi:=Xi+A[i,j]*Y[Iy[j]]; for j:=i+1 to pred(DimMat) do Xi:=Xi-A[i,j]*PVX[j]; PVX[i]:=Xi/A[i,i]; end; for i:=0 to pred(DimMat) do X[Jx[i]]:=PVX[i]; Setlength(PVX,0); ErreurMat:=0; end else ErreurMat:=MatDebordTas; end; PROCEDURE SysLin(A:RCOMat;var X:RCOVec;B:RCOVec;var Det:extended); var AT : RCOMat; Jx,Iy : RCOVecE; PROCEDURE Alloc; begin SetLength(AT,DimMat,DimMat); SetLength(Jx,DimMat); SetLength(Iy,DimMat); end; PROCEDURE DesAlloc; begin SetLength(Iy,0); SetLength(Jx,0); SetLength(At,0,0); end; begin Init; if true then begin Alloc; TrianguleO(A,AT,Jx,Iy,Det); if Det<>0 then ResouTrO(AT,X,B,Jx,Iy); DesAlloc; end else ErreurMat:=MatDebordTas; end; PROCEDURE InvMat(A:RCOMat;var B:RCOMat;var Det:extended); var j : integer; AT : RCOMAt; PVX : RCOVec; Jx,Iy : RCOVecE; PROCEDURE Alloc; begin Setlength(AT,DimMat,DimMat); SetLength(PVX,DimMat); SetLength(Jx,DimMat); SetLength(Iy,DimMat); end; PROCEDURE DesAlloc; begin SetLength(Iy,0); SetLength(Jx,0); SetLength(PVX,0); SetLength(AT,0,0); end; PROCEDURE initVX; var i : integer; begin for i:=0 to pred(DimMat) do PVX[i]:=0; PVX[j]:=1; end; PROCEDURE TransfertCol; var i : integer; begin for i:=0 to pred(DimMat) do B[i,j]:=PVX[i]; end; begin Init; if true then begin Alloc; TrianguleO(A,AT,Jx,Iy,Det); if ErreurMat=0 then begin for j:=0 to pred(DimMat) do begin InitVX; ResouTrO(AT,PVX,PVX,Jx,Iy); TransfertCol; end; end; DesAlloc; end else ErreurMat:=MatDebordTas; end; initialization Ini:=false; DimMat:=0; END. // Test program : { procedure TForm1.FormCreate(Sender: TObject); Const Dim = 200; var A,B,C : RCOMat; V,W,U : RCOVec; i,j : integer; Det : extended; begin InitMat(Dim); SetLength(A,Dim,Dim); SetLength(B,Dim,Dim); SetLength(C,Dim,Dim); Randomize; for i:=0 to pred(Dim) do begin V[i]:=random*10; for j:=0 to pred(Dim) do A[i,j]:=random*10; end; InvMat(A,B,Det); MatMat(A,B,C); // [C] should be the unit matrix end; }