連分数
連分数は分母に更に分数が含まれているような分数です。
分子の値が全て1の場合正則連分数と呼ばれます。
表記方法としては次のようなものもあります。
連分数で、πの値や、三角関数、平方根の値を表すことができ、小数点を含む実数を簡単に連分数に変換することが出来ます。
詳細については、webで"連分数"を検索してください。
左図は、実数値を連分数に変換した場合の実行例です。
三角関数値としては、tan(x)を取り上げています。
big_mathでは、tan(x)を級数展開で求めていますが、連分数による方法の方が高速なのかもしれません。
プログラム
Bigdecimalの組み込みはベルヌーイ数 その4を参照して下さい。
unit Main; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; continued_fraction_btn1: TButton; Edit1: TEdit; Edit2: TEdit; tanx2_btn: TButton; continued_fraction_btn2: TButton; Tanx3_btn: TButton; tanx1_btn: TButton; Edit3: TEdit; LnX1_Btn: TButton; LnX2_btn: TButton; procedure continued_fraction_btn1Click(Sender: TObject); procedure tanx2_btnClick(Sender: TObject); procedure continued_fraction_btn2Click(Sender: TObject); procedure Tanx3_btnClick(Sender: TObject); procedure tanx1_btnClick(Sender: TObject); procedure LnX1_BtnClick(Sender: TObject); procedure LnX2_btnClick(Sender: TObject); private { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} uses Velthuis.Bigdecimals, Velthuis.BigIntegers, math; //-------------------------------------------------------------- // tanの連分数展開計算 // tan(x) x radian // tan(x) = x / // 1 - x^2 / // 3 - x^2 / 5,7,9 procedure TForm1.tanx1_btnClick(Sender: TObject); const N = 15; var i : integer; x, t : double; c, b : array[0..N - 1] of double; // tan(x) function ascend(n: integer): double; var f, bk : double; k : integer; begin bk := 0; f := 0; // 初期値 for k := n downto 0 do begin // 後ろから計算 bk := b[k] + f; if bk = 0 then break; f := c[k] / bk; end; if bk = 0 then result := infinity else result := f; end; begin val(edit2.Text, x, i); if i <> 0 then begin application.MessageBox('入力値に間違いが有ります。','注意',0); exit; end; memo1.Clear; c[0] := x; for i := 1 to N - 1 do c[i] := -x * x; // x, -x^2, -x^2, ・・・ for i := 0 to N - 1 do b[i] := 2 * (i + 1) - 1; // 1, 3, 5, 7, ・・・ 奇数 t := ascend(N - 1); // 連分数計算 Memo1.Lines.Append('tan(x) = ' + floattostr(t)); end; // tan(x) // 途中結果出力あり procedure TForm1.tanx2_btnClick(Sender: TObject); const N = 18; var i : integer; x, t : double; // tan(x) 計算途中経過出力あり function descend(x: double; n: integer): double; var i : integer; p1, q1, p2, q2, p3, q3 : double; ci, bi, p2b : double; begin p1 := 1; // 初期値 分子1 q1 := 0; // 初期値 分母1 p2 := 0; // 初期値 分子2 q2 := 1; // 初期値 分母2 p2b := 0; for i := 1 to n do begin // 先頭から計算 if i = 1 then ci := x // ci= x, -x^2, -x^2 ・・・ else ci := -x * x; bi := 2 * i - 1; // bi= 1,3,5,7,9・・・奇数 p3 := p1 * ci + p2 * bi; // 分子計算 p1 := p2; p2 := p3; q3 := q1 * ci + q2 * bi; // 分母計算 q1 := q2; q2 := q3; if q2 <> 0 then begin // 分母がゼロでなければ p1 := p1 / q2; q1 := q1 / q2; p2 := p2 / q2; q2 := 1; Form1.Memo1.Lines.Append(inttostr(i) + ': ' + floattostr(p2)); if p2b = p2 then break; // 同じ値が続いたら終了 p2b := p2; end else begin // 分母がゼロだったら Form1.Memo1.Lines.Append('無限大'); break; end; end; if q2 <> 0 then result := p2 else result := infinity; end; begin val(edit2.Text, x, i); if i <> 0 then begin application.MessageBox('入力値に間違いが有ります。','注意',0); exit; end; memo1.Clear; t := descend(x, N); // 途中結果出力あり計算 Memo1.Lines.Append('tan(x) =' + floattostr(t)); end; // tan(x) 再帰ループ計算 procedure TForm1.Tanx3_btnClick(Sender: TObject); var x, t : double; ch : integer; // x radian // x2 x^2 // t 連分数 // n 再起数 nの1/2 n は奇数 function drTan(x, x2, t: double; n: integer): double; begin if n - t = 0 then t := infinity else t := x2 / (n - t); n := n - 2; if (n <= 1) then begin if (1 - t) = 0 then result := infinity else result := x / (1 - t); end else result := drTan(x, x2, t, n); end; begin val(edit2.Text, x, ch); if ch <> 0 then begin application.MessageBox('入力値に間違いが有ります。','注意',0); exit; end; memo1.Clear; t := drtan(x, x * x, 0, 21); // 再起計算 Memo1.Lines.Append('再帰ループ計算'); Memo1.Lines.Append('tan(x) =' + floattostr(t)); end; //------------------------------------------------------------------------- // ln(x) // ln(1+x) = x/ // 1+x/ // 2+x/ // 3+2x/ // 2+2x/ // 5+3x/ // 2+3x/ // 7+ ・・・ procedure TForm1.LnX1_BtnClick(Sender: TObject); const N = 28; var xin, x : double; ch : integer; t, Lnx : double; // 対数関数 再帰 後ろから計算 function drLog(x: double; n: integer; t: double): double; var n2 :Integer; x2 :double; begin n2 := n; x2 := x; if (n > 3) then begin if (n mod 2 = 0) then n2 := 2; x2 := x * (n div 2); end; t := x2 / (n2 + t); if (n <= 2) then result := x / (1 + t) else result := drLog(x, n - 1, t); end; begin val(edit3.Text, xin, ch); // 入力値確認 if ch <> 0 then begin application.MessageBox('入力値に間違いが有ります。','注意',0); exit; end; if xin <= 0 then begin application.MessageBox('無効な入力です。','注意',0); exit; end; Memo1.Clear; t := 0; x := xin - 1; LnX := drLog(x, n, t); Memo1.Lines.Append('再帰計算'); Memo1.Lines.Append('Len(x) =' + floattostr(Lnx)); end; // ln(x) // ln(1+x) = x/ // 1+1^2x/ // 2+1^2x/ // 3+2^2/ // 4+2^2/ // 5+3^2x/ // 6+3^2x/ // 7+4^2/ // 8+4^2/ // 9+ ・・・・・ procedure TForm1.LnX2_btnClick(Sender: TObject); const N = 28; var xin : double; ch : integer; Lnx : double; // ln(x) function drln(x: double; n: integer): double; var nx, i : integer; t : double; begin t := 0; for i := n downto 2 do begin // n -> 2 後ろから計算 nx := i div 2; t := nx * nx * x / (i + t) end; result := x / (1 + t); // n=1 end; begin val(edit3.Text, xin, ch); // 入力値確認 if ch <> 0 then begin application.MessageBox('入力値に間違いが有ります。','注意',0); exit; end; if xin <= 0 then begin application.MessageBox('無効な入力です。','注意',0); exit; end; Memo1.Clear; Lnx := drln(xin - 1, 28); Memo1.Lines.Append('Len(x) =' + floattostr(Lnx)); end; //------------------------------------------------------------------------- // 実数の連分数 procedure TForm1.continued_fraction_btn1Click(Sender: TObject); var xin : double; x, b, a, t, one : bigdecimal; ch : integer; begin val(edit1.Text, xin, ch); // 入力値確認 if ch <> 0 then begin application.MessageBox('入力値に間違いが有ります。','注意',0); exit; end; memo1.Clear; memo1.Lines.Append('実数値 = ' + floattostr(xin)); x := edit1.Text; b := x; t := '1e-30'; // 収束判定値 one := '1'; while True do begin a := b.Int; // 小数部切り捨て memo1.Lines.Append(a.ToPlainString + '+1/'); b := b - a; // 小数部のみ取り出し if bigdecimal.Abs(b) < t then break; b := one / b; end; end; // 実数の連分数 procedure TForm1.continued_fraction_btn2Click(Sender: TObject); var a : array of bigdecimal; // 連分数 t : array of array of bigdecimal; // 分数 分子分母 xb : bigdecimal; k : integer; s : string; xin : double; procedure ContFrac(x : bigdecimal); // 連分数計算 const M = 32; // 最大ループ数 var b, one, h : bigdecimal; i : integer; begin one := '1'; h := '1e-30'; // 収束判定値 for i := 1 to M do begin b := x.Int; // 小数点以下切り捨て setlength(a, i); a[i - 1] := b; // 連分数 配列a x := x - b; // xの小数部 if bigdecimal.Abs(x) < h then break; x := one / x; end; end; procedure ApproximateRatio; // 近似分数(比)計算 分子 & 分母 var p0, p1, p2 : bigdecimal; q0, q1, q2 : bigdecimal; one : bigdecimal; i, j, k : integer; begin one := '1'; p0 := a[0]; // 分子 q0 := one; // 分母 p1 := a[1] * p0 + one; q1 := a[1]; j := length(a); setlength(t, j, 2); t[0, 0] := p0; // 分子 t[0, 1] := q0; // 分母 t[1, 0] := p1; t[1, 1] := q1; delete(a, 0 , 2); // 配列a 先頭2個削除 j := length(a); for k := 0 to j - 1 do begin p2 := a[k] * p1 + p0; // 分子 q2 := a[k] * q1 + q0; // 分母 i := k + 2; t[i, 0] := p2; // 分子 t[i, 1] := q2; // 分母 p0 := p1; p1 := p2; q0 := q1; q1 := q2; end; end; begin val(edit1.Text, xin, k); // 入力値確認 if k <> 0 then begin application.MessageBox('入力値に間違いが有ります。','注意',0); exit; end; Memo1.Clear; memo1.Lines.Append('実数値 = ' + floattostr(xin)); xb := edit1.Text; ContFrac(xb); // 連分数計算 結果配列a s := ''; for xb in a do s := s + xb.ToPlainString + '+1/ '; memo1.Lines.Append(s); ApproximateRatio; // 連分数から分数 分子分母計算 結果配列t for k := 0 to length(t) - 1 do begin if t[k, 0] <> bigdecimal.Zero then begin // ゼロ表示'0.0'の為ゼロ以外表示調整 xb := t[k, 0] / t[k, 1]; xb := xb.RoundToScale(20, rmNearestUp); // 小数点以下20桁で丸め xb := xb.RemoveTrailingZeros(1); // 不要なゼロ削除 s := xb.ToPlainString; end else s := '0.0'; memo1.Lines.Append(t[k, 0].ToPlainString +' / ' + t[k, 1].ToPlainString + ' = ' + s); end; end; end.
continued_fraction.zip
三角関数、逆三角関数、その他関数 に戻る