Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

Решение СЛАУ, вычисление обратных матриц и определителей с использованием LU-разложения

01.01.2007
unit LU_Utils;
 
interface
uses Classes;
type
  TDblArr = array of Double;
  TIntArr = array of Integer;
  TDblMatr = array of array of Double;
 
procedure CopyMatr(Src, Dest: TDblMatr);
 
//исходная матрица разрушается, при необходимости нужно сделать копию
procedure LUDecomp(Matr: TDblMatr; Indx: TIntArr; var DSign: Integer);
procedure LUBackSubst(Matr: TDblMatr; Indx: TIntArr; Reslt: TDblArr);
 
//решение системы линейных уравнений. XY на входе - правые части,
//на выходе - вектор решений
//LU-разложение матрицы может использоваться многократно с
//разными правыми частями (при модификации кода)
procedure SLAUSolver(Matr: TDblMatr; XY: TDblArr);
 
//исходная матрица заменяется обратной
procedure MatrInverse(Matr: TDblMatr);
 
//исходная матрица сохраняется
function Determinant(Matr: TDblMatr): Double;
 
implementation
uses SysUtils;
 
procedure CopyMatr(Src, Dest: TDblMatr);
var
  n, i: Integer;
begin
  n := Length(Src);
  for i := 0 to n - 1 do
    Move(Src[i, 0], Dest[i, 0], n * SizeOf(Double));
end;
 
procedure LUDecomp(Matr: TDblMatr; Indx: TIntArr; var DSign: Integer);
const
  Tiny = 1.0E-40;
var
  n, k, j, imax, i: Integer;
  sum, dum, big: Double;
  VV: TDblArr;
  DumPtr: Pointer;
begin
  n := Length(Matr);
  SetLength(VV, n);
  DSign := 1;
 
  for i := 0 to n - 1 do begin
    big := 0;
    for j := 0 to n - 1 do
      if (abs(Matr[i, j]) > big) then
        big := Abs(Matr[i, j]);
    if (big = 0.0) then
      raise Exception.Create('Stopped. Matrix is singular!!!');
    VV[i] := 1 / big;
  end;
 
  for j := 0 to n - 1 do begin
    for i := 0 to j - 1 do begin
      sum := Matr[i, j];
      for k := 0 to i - 1 do
        sum := sum - Matr[i, k] * Matr[k, j];
      Matr[i, j] := sum;
    end;
 
    big := 0;
    for i := j to n - 1 do begin
      sum := Matr[i, j];
      for k := 0 to j - 1 do
        sum := sum - Matr[i, k] * Matr[k, j];
      Matr[i, j] := sum;
      dum := VV[i] * abs(sum);
      if (dum > big) then begin
        big := dum;
        imax := i;
      end;
    end;
 
    if (j <> imax) then begin
      DumPtr := Pointer(Matr[imax]);
      Pointer(Matr[imax]) := Pointer(Matr[j]);
      Pointer(Matr[j]) := DumPtr;
      DSign := -DSign;
      VV[imax] := VV[j]
    end;
 
    Indx[j] := imax;
    if (Matr[j, j] = 0) then
      Matr[j, j] := Tiny;
    if (j <> n - 1) then begin
      dum := 1 / Matr[j, j];
      for i := j + 1 to n - 1 do
        Matr[i, j] := Matr[i, j] * dum;
    end;
  end;
end;
 
procedure LUBackSubst(Matr: TDblMatr; Indx: TIntArr; Reslt: TDblArr);
var
  n, j, ip, ii, i: Integer;
  sum: Double;
begin
  n := Length(Matr);
  ii := -1;
 
  for i := 0 to n - 1 do begin
    ip := Indx[i];
    sum := Reslt[ip];
    Reslt[ip] := Reslt[i];
    if (ii <> -1) then
      for j := ii to i - 1 do
        sum := sum - Matr[i, j] * Reslt[j]
    else
      if (sum <> 0.0) then
        ii := i;
    Reslt[i] := sum;
  end;
 
  for i := n - 1 downto 0 do begin
    sum := Reslt[i];
    if (i < n - 1) then
      for j := i + 1 to n - 1 do
        sum := sum - Matr[i, j] * Reslt[j];
    Reslt[i] := sum / Matr[i, i];
  end
end;
 
procedure SLAUSolver(Matr: TDblMatr; XY: TDblArr);
var
  Indx: TIntArr;
  DSign: Integer;
begin
  SetLength(Indx, Length(XY));
  LUDecomp(Matr, Indx, DSign);
  LUBackSubst(Matr, Indx, XY);
end;
 
procedure MatrInverse(Matr: TDblMatr);
var
  n, i, j: Integer;
  Indx: TIntArr;
  Col: TDblArr;
  DSign: Integer;
  Temp: TDblMatr;
begin
  n := Length(Matr);
  SetLength(Indx, n);
  SetLength(Col, n);
  SetLength(Temp, n, n);
  LUDecomp(Matr, Indx, DSign);
  for j := 0 to n - 1 do begin
    for i := 0 to n - 1 do
      Col[i] := 0;
    Col[j] := 1;
    LUBackSubst(Matr, Indx, Col);
    for i := 0 to n - 1 do
      Temp[i, j] := Col[i];
  end;
  CopyMatr(Temp, Matr);
  Temp := nil;
end;
 
function Determinant(Matr: TDblMatr): Double;
var
  n, j: Integer;
  Indx: TIntArr;
  DSign: Integer;
  Temp: TDblMatr;
begin
  n := Length(Matr);
  SetLength(Indx, n);
  SetLength(Temp, n, n);
  CopyMatr(Matr, Temp);
  LUDecomp(Temp, Indx, DSign);
  Result := DSign;
  for j := 0 to n - 1 do
    Result := Result * Temp[j, j];
end;
 
end.
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, LU_Utils, StdCtrls;
 
type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
function ShowArr(Arr: TDblArr): string;
var
  i: Integer;
begin
  Result := '';
  for i := 0 to Length(Arr) - 1 do
    Result := Result + FormatFloat('0.0000', Arr[i]) + '  ';
end;
 
procedure ShowMatr(Matr: TDblMatr; Strings: TStrings);
var
  n, i, j: Integer;
  s: string;
begin
  n := Length(Matr);
  for i := 0 to n - 1 do begin
    s := '';
    for j := 0 to n - 1 do
      s := s + FormatFloat('0.0000', Matr[i, j]) + '  ';
    Strings.Add(s);
  end;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  M:TDblMatr;
  B:TDblArr;
begin
SetLength(M,3,3);
SetLength(B,3);
  M[0, 0] := 0.5;
  M[0, 1] := 2;
  M[0, 2] := -1;
  M[1, 0] := -15;
  M[1, 1] := -0.1;
  M[1, 2] := 3.2;
  M[2, 0] := 0.7;
  M[2, 1] := 4.1;
  M[2, 2] := -0.2;
  B[0] := 11.4;
  B[1] := -196.02;
  B[2] := 10.22;
  Memo1.Clear;
  ShowMatr(M, Memo1.Lines);
  Memo1.Lines.Add('');
  SLAUSolver(M, B);
  ShowMatr(M, Memo1.Lines);
  Memo1.Lines.Add('');
  Memo1.Lines.Add(ShowArr(B));
end;
 
procedure TForm1.Button2Click(Sender: TObject);
var
  M:TDblMatr;
begin
  SetLength(M,3,3);
  M[0, 0] := 1;
  M[0, 1] := 1;
  M[0, 2] := 1;
  M[1, 0] := 1;
  M[1, 1] := 2;
  M[1, 2] := 1;
  M[2, 0] := 1;
  M[2, 1] := 1;
  M[2, 2] := 3;
  Memo1.Clear;
  ShowMatr(M, Memo1.Lines);
  Memo1.Lines.Add('');
  MatrInverse(M);
  ShowMatr(M, Memo1.Lines);
end;
 
procedure TForm1.Button3Click(Sender: TObject);
var
  M:TDblMatr;
begin
  SetLength(M,3,3);
  M[0, 0] := 1;
  M[0, 1] := 0;
  M[0, 2] := 1;
  M[1, 0] := 0;
  M[1, 1] := 2;
  M[1, 2] := 0;
  M[2, 0] := 1;
  M[2, 1] := 0;
  M[2, 2] := 3;
  Memo1.Clear;
  ShowMatr(M, Memo1.Lines);
  Memo1.Lines.Add('');
  Memo1.Lines.Add(FormatFloat('0.0000', Determinant(M)));
end;
 
end.

Автор: MBo