Matrice.pas

// 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;
}