連立一次方程式の解法7
連立一次方程式の解法の続きです。
今回は、連立一次方程式の解法5をBigDecimalに変更したものです。
行列式の計算の中には、特に問題になる演算はなく、ExtendedをBigDecimalに変更すれば正しい演算結果を得られます。
単に、計算結果の有効桁数が増えるだけです。
有効桁数の入力を60迄に制限していますが、単に表示の問題だけで制限しているので変更しても問題ありません。
プログラム
unit Main; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons, Vcl.Grids, system.UITypes, Vcl.ExtCtrls, Velthuis.BigIntegers, Velthuis.BigDecimals; type TForm1 = class(TForm) BitBtn1: TBitBtn; Memo1: TMemo; StringGrid1: TStringGrid; StringGrid2: TStringGrid; LabeledEdit1: TLabeledEdit; BitBtn2: TBitBtn; RadioGroup1: TRadioGroup; LabeledEdit2: TLabeledEdit; LabeledEdit3: TLabeledEdit; CheckBox1: TCheckBox; procedure BitBtn1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure BitBtn2Click(Sender: TObject); procedure RadioGroup1Click(Sender: TObject); private { Private 宣言 } function LUecomposition: byte; procedure GridSet(Sin, Colin: integer); // グリッドサイズ変更 procedure testset; public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} var Nltest : integer = 4; // テストデーター行列数 // テストデーター mattestA : array[0..3] of array[0..3] of extended = ((8, 16, 24, 32), (2, 7, 12, 17), (6, 17, 32, 59), (7, 22, 46,105)); mattestB : array[0..3] of array[0..3] of extended = ((8, 16, 24, 32), (2, 7, 12, 17), (2, 17, 32, 0), (7, 0, 0, 105)); mattestC : array[0..3] of array[0..3] of extended = (( 8, 34, 24, 32), ( 2, 7, 0, 0), (24, 17, 0, 0), ( 7, 0, 0, 0)); mattestD : array[0..3] of array[0..3] of extended = ((0, 16, 24, 32), (2, 7, 12, 17), (6, 17, 32, 0), (7, 0, 0, 105)); mattestE : array[0..3] of array[0..3] of extended = ((0, 0, 0, 32), (0, 0, 12, 17), (6, 17, 32, 0), (7, 0, 0, 105)); btestA : array[0..3] of extended = (160, 70, 198, 291); btestB : array[0..3] of extended = (150, 60, 10, 40); Nl : integer; // 行列数 aa : array of array of bigdecimal; // 演算用a配列 a : array of array of bigdecimal; // 演算用a配列 b : array of bigdecimal; // 演算用b行列 p : array of integer; // 行移動 dpcs : integer; allowable_errorr : bigdecimal; // グリッドの設定 procedure TForm1.GridSet(Sin, Colin: integer); // グリッドサイズ変更 var // データー行の数は10迄 H, W, i :integer; // それ以上はスクロールする begin inc(Sin); inc(Colin); StringGrid1.ColCount := Colin; StringGrid1.RowCount := Sin; if Sin <= 11 then // ストリンググリッドの大きさ設定 begin // 固定行を含め11行又はそれ以下の場合 H := (StringGrid1.DefaultRowHeight + 1) * Sin; StringGrid2.ScrollBars := ssNone; // スクロールバー表示 end else begin // 固定行を含め11行より多い場合 H := (StringGrid1.DefaultRowHeight + 1) * (10 + 1); StringGrid2.ScrollBars := ssVertical; // スクロールバー表示 end; if Colin <= 11 then // ストリンググリッドの大きさ設定 begin // 固定行を含め11行又はそれ以下の場合 W := (StringGrid1.DefaultColWidth + 1) * Colin; StringGrid1.ScrollBars := ssNone; // スクロールバーなし end else begin // 固定行を含め11行より多い場合 W := (StringGrid1.DefaultColWidth + 1) * (10 + 1); StringGrid1.ScrollBars := ssBoth; // スクロールバー表示 end; StringGrid1.ClientWidth := W; StringGrid1.ClientHeight := H; for i := 1 to Sin - 1 do StringGrid1.Cells[0,i] := 'a' + intTostr(i) + '*'; for i := 1 to Sin - 1 do StringGrid1.Cells[i,0] := 'a*' + intTostr(i); StringGrid2.RowCount := Sin; StringGrid2.ClientHeight := H + 1; StringGrid2.ClientWidth := (StringGrid2.DefaultColWidth + 1) * 2; for i := 1 to Sin - 1 do StringGrid2.Cells[0, i] := intTostr(i); StringGrid2.Cells[1, 0] := 'b*='; end; // ガウスの消去法による連立方程式の解法 LU分解 function TForm1.LUecomposition: byte; const sp = ' '; var i, j, k, n, pivot, pt: integer; sum, tmp, amax : bigdecimal; x, y : array of bigdecimal; LL, spl : string; begin setlength(a, Nl, Nl); setlength(x, Nl); setlength(y, Nl); setlength(p, Nl); memo1.Clear; // matA, bデーターを編集用bigdecimalにコピー // aの値は変化してしまうため for j := 0 to Nl - 1 do begin for k := 0 to Nl - 1 do begin a[j, k] := aa[j, k]; end; end; // 行入れ替えno初期化 for i := 0 to Nl - 1 do p[i] := i; // 行番号0~N - 1 // LU分解 前進消去 for k := 0 to Nl - 2 do begin pivot := k; // ピボットの選択 amax := bigdecimal.abs(a[pivot, pivot]); // K列の行の最大値検索 for i := pivot + 1 to Nl - 1 do begin // ピボットの次の行から if bigdecimal.abs(a[i, k]) > amax then begin pivot := i; amax := bigdecimal.abs(a[pivot, k]); end; end; if pivot <> k then begin // 最大値行が違ったら入れ替え for i := 0 to Nl - 1 do begin tmp := a[k, i]; a[k, i] := a[pivot, i]; a[pivot, i] := tmp; end; pt := p[k]; // 行番号交換 p[k] := p[pivot]; p[pivot] := pt; end; // LU decomposition right-looking algorithm for i := k + 1 to Nl - 1 do begin if a[k, k] = 0 then begin // 対角にゼロが発生したら終了 result := 1; exit; end; a[i, k] := a[i, k] / a[k, k]; // L for j := k + 1 to Nl - 1 do a[i, j] := a[i, j] - a[i, k] * a[k, j]; // U end; end; // bの値セット for i := 0 to Nl - 1 do x[i] := b[p[i]]; // 行入れ替えnoによりxにbの値セット // 前進代入 for i := 1 to Nl - 1 do for j := 0 to i - 1 do x[i] := x[i] - a[i, j] * x[j]; // 後退代入 for i := Nl - 1 downto 0 do begin for j := i + 1 to Nl - 1 do x[i] := x[i] - a[i, j] * x[j]; if a[i, i] = 0 then begin // 対角ゼロが発生したら終了 result := 1; exit; end; x[i] := x[i] / a[i, i]; end; // 各行代入答えの差分計算 for j := 0 to Nl - 1 do begin sum := 0; for i := 0 to Nl - 1 do begin sum := sum + aa[j, i] * x[i]; // 元のaa[]の値を使用します。 end; y[j] := b[j] - sum; // 差分 end; LL := '計算順 '; for i := 0 to Nl - 1 do begin if i < NL - 1 then LL := LL + intTostr(p[i] + 1) + ',' else LL := LL + intTostr(p[i] + 1); end; memo1.Lines.Append(LL); // 行番号表示 memo1.Lines.Append('行 No 検算による差分'); for k := 0 to Nl - 1 do begin LL := 'No = ' + inttostr(k + 1); n := 10 - length(LL); spl := ''; for j := 1 to n do spl := spl + sp; memo1.Lines.Append(LL + spl + y[k].ToString); end; // 誤差の確認 誤差が大きい場合演算途中対角の値がゼロに近い値になっています。 j := 0; // 誤差大の数 for k := 0 to Nl - 1 do begin if bigdecimal.abs(b[k]) > allowable_errorr then amax := y[k] / b[k] // 誤差比率 else amax := y[k]; if bigdecimal.abs(amax) > allowable_errorr then inc(j); end; if checkbox1.Checked = false then if j > 0 then begin memo1.Lines.Append('検算結果 NG'); result := 1; exit; end else memo1.Lines.Append('検算結果 OK'); // xの値表示 for k := 0 to Nl - 1 do begin x[k] := x[k].RemoveTrailingZeros(0); memo1.Lines.Append('X' + inttostr(k + 1) + '=' + x[k].ToString); end; result := 0; end; procedure TForm1.BitBtn1Click(Sender: TObject); var i, j, ch: integer; matA : array of array of extended; // A行列配列 入力チェック用 演算には使用されない matB : array of extended; // A行列配列 入力チェック用 演算には使用されない maxb, minb, bc, epsilon : bigdecimal; LL : string; acmax : array of bigdecimal; acmin : array of bigdecimal; begin setlength(matA, Nl, Nl); setlength(aa, Nl, Nl); setlength(matB, Nl); setlength(b, Nl); setlength(acmax, Nl); setlength(acmin, Nl); // matAにグリッド1から値読み込み for i := 0 to Nl - 1 do for j := 0 to Nl - 1 do begin val(StringGrid1.Cells[i + 1,j + 1],matA[j,i], ch); if ch <> 0 then begin MessageDlg('a' + intTostr(j + 1) + ',' + intTostr(i + 1) + ' の入力値の' + intTostr(ch) + '文字目に誤りが有ります。', mtInformation,[mbOk], 0); exit; end; end; // bにグリッド2から値読み込み for i := 0 to Nl - 1 do begin val(StringGrid2.Cells[1, i + 1], matB[i], ch); if ch <> 0 then begin MessageDlg('b' + intTostr(i + 1) + 'の入力値の' + intTostr(ch) + '文字目に誤りが有ります。', mtInformation,[mbOk], 0); exit; end; end; // bigdecimalの配列に読み込み for i := 0 to Nl - 1 do begin b[i] := StringGrid2.Cells[1, i + 1]; for j := 0 to Nl - 1 do begin aa[j, i] := StringGrid1.Cells[i + 1,j + 1]; end; end; val(labelededit2.Text, dpcs, ch); if ch <> 0 then begin MessageDlg('有効桁数入力値' + intTostr(ch) + '文字目に誤りが有ります。', mtInformation,[mbOk], 0); exit; end; if dpcs < 5 then begin MessageDlg('有効桁数の値が小さすぎます。', mtInformation,[mbOk], 0); exit; end; if dpcs > 60 then begin MessageDlg('有効桁数のは60迄にしてください。', mtInformation,[mbOk], 0); exit; end; // 誤差の許容範囲読み込み val(labelededit3.Text, matB[0], ch); if ch <> 0 then begin MessageDlg('許容誤差の' + intTostr(ch) + '文字目に誤りが有ります。', mtInformation,[mbOk], 0); exit; end; if matB[0] > 10 then begin MessageDlg('許容誤差の値が大きすぎます。', mtInformation,[mbOk], 0); exit; end; if matB[0] <= 0 then begin MessageDlg('許容誤差はゼロより大きくして下さい。', mtInformation,[mbOk], 0); exit; end; allowable_errorr := labelededit3.Text; // epsilonを有効桁数から設定 LL := '1e-' + intTostr(dpcs - 1); epsilon := LL; // A行列の最大値最小値検索 for i := 0 to Nl -1 do begin acmax[i] := 1; acmin[i] := 1; if aa[i, 0] <> 0 then acmax[i] := bigdecimal.abs(aa[i, 0]); if aa[i, 0] <> 0 then acmin[i] := bigdecimal.abs(aa[i, 0]); for j := 1 to Nl - 1 do begin if (bigdecimal.abs(aa[i, j]) > acmax[i]) and (aa[i, j] <> 0) then acmax[i] := bigdecimal.abs(aa[i, j]); if (bigdecimal.abs(aa[i, j]) < acmin[i]) and (aa[i, j] <> 0) then acmin[i] := bigdecimal.abs(aa[i, j]); end; end; // B行列とA行列の比最小値と最大値検索 maxb := bigdecimal.abs(b[0]) / acmax[0]; minb := bigdecimal.abs(b[0]) / acmin[0]; for i := 1 to Nl - 1 do begin if bigdecimal.abs(b[i]) / acmax[i] > maxb then maxb := bigdecimal.abs(b[i]) / acmax[i]; if bigdecimal.abs(b[i]) / acmin[i] < minb then minb := bigdecimal.abs(b[i]) / acmin[i]; end; if minb > 1 then minb := 1 / minb; if maxb < 1 then maxb := 1 / maxb; // bの値の範囲が演算桁数の範囲を越えているか判定 bc := minb; if (minb = 0) and (maxb > 0) then if maxb >= 1 then bc := 1 / maxb else bc := maxb; if (minb > 0) and (maxb > 0) then bc := minb / maxb; if (minb = 0) and (maxb = 0) then bc := 1; if checkbox1.Checked = false then if bc < epsilon then begin MessageDlg('bの入力値の値が演算の範囲を超えています。', mtInformation,[mbOk], 0); memo1.Clear; exit; end; // 検算結果の判定値設定 allowable_errorr := allowable_errorr * bigdecimal.Sqrt(bc, dpcs * 2); bigdecimal.DefaultPrecision := dpcs; // 連立方程式の解法 解法できない場合あり j := LUecomposition; if (j = 0) or (j = 2) then exit; // 対角値 0 なら終了 // 解法出来なかったら if j = 1 then begin MessageDlg('解法時対角ゼロか、ゼロに近い値が発生しました、入力値を見直して下さい。' + #13#10 + '有効桁数が不足している場合もあります。' + #13#10 + '有効桁数が小さい場合は、許容誤差の値を見直して下さい。', mtInformation,[mbOk], 0); exit; end; end; // テストデーターの選択設定 procedure TForm1.testset; var i, j : integer; begin GridSet(Nltest, Nltest); Nl := Nltest; for i := 1 to Nltest do for j := 1 to Nltest do begin case RadioGroup1.ItemIndex of 0: StringGrid1.Cells[i,j] := floatTostr(mattestA[j-1, i-1]); 1: StringGrid1.Cells[i,j] := floatTostr(mattestB[j-1, i-1]); 2: StringGrid1.Cells[i,j] := floatTostr(mattestC[j-1, i-1]); 3: StringGrid1.Cells[i,j] := floatTostr(mattestD[j-1, i-1]); 4: StringGrid1.Cells[i,j] := floatTostr(mattestE[j-1, i-1]); end; end; for i := 1 to Nltest do begin case RadioGroup1.ItemIndex of 0: StringGrid2.Cells[1,i] := floatTostr(btestA[i-1]); 1, 2, 3, 4: StringGrid2.Cells[1,i] := floatTostr(btestB[i-1]); end; end; LabeledEdit1.Text := intTostr(Nltest); memo1.Clear; end; // グリッドサイズの変更 procedure TForm1.BitBtn2Click(Sender: TObject); var ch : integer; begin val(LabeledEdit1.Text, Nl, ch); if ch <> 0 then begin MessageDlg('N数の入力値の' + intTostr(ch) + '文字目に誤りが有ります。', mtInformation,[mbOk], 0); exit; end; GridSet(Nl, Nl); end; procedure TForm1.RadioGroup1Click(Sender: TObject); begin testset; end; // 初期値の設定 procedure TForm1.FormCreate(Sender: TObject); begin testset; end; end.
LU_Bigdecimal.zip
三角関数、逆三角関数、その他関数、 連立一次方程式の解法 に戻る