第2種球ベッセル関数
ベッセル関数の詳細については、Webで検索して参照してください。
左図は第2種の球ベッセル関数の計算式ですが、第2種ベッセル関数の実数計算により簡単に求めることが出来ます。
第2種ベッセル関数の計算については、第2種ベッセル関数を参照すれば分かります。
次の図は、プログラムの実行画面です。
Xの値が大きくなると、計算の有効桁数の関係で、誤差が大きくなるのは第2種ベッセル関数と変わりません。
あまり大きな値を与えると、誤差の方が大きくなります。
プログラムは、第2種ベッセル関数から、第2種球ベッセルに変更するにあたって、計算精度向上の為、ガンマ関数、ディガンマ関数の計算方法を変更してあります。
ガンマ関数は、対数計算、ディガンマ関数は、ゼロ以上の整数計算にしました。
第2種ベッセル関数も変更すれは、計算精度が向上します。
プログラム
// Xの値 積分計算の上限値MNによってオーバーフローを発生するので // コンパイル設定にオーバーフローチックが必須です。 // 第二種ベッセル関数の場合次数vが整数の時と、非整数の時で、別の計算をします。 // Xの値が大きくなると誤差が大きくなります 演算有効桁数不足 unit Main; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls, Vcl.Buttons, system.Math, system.UITypes, VclTee.TeeGDIPlus, VCLTee.TeEngine, VCLTee.Series, VCLTee.TeeProcs, VCLTee.Chart; type TForm1 = class(TForm) BitBtn1: TBitBtn; Memo1: TMemo; LabeledEdit1: TLabeledEdit; Chart1: TChart; Series1: TLineSeries; CheckBox1: TCheckBox; LabeledEdit2: TLabeledEdit; Series2: TPointSeries; Button1: TButton; CheckBox3: TCheckBox; CheckBox2: TCheckBox; procedure BitBtn1Click(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private 宣言 } public { Public 宣言 } procedure GraphNnX(xmax, v: extended); procedure GraphKnX(xmax, v:extended); end; var Form1: TForm1; implementation {$R *.dfm} const // 誤差計算用データー k05x: array[0..10] of extended = (infinity, 0.4802524860099896844602, 0.07891087361460348685438, 0.01849841602458628046582, 0.004980195513475615411195, 0.001443218477709391358495, 4.377616999209552134537E-4, 1.369687289039624644564E-4, 4.382884545220336481172E-5, 1.426538241889837020678E-5, 4.70533267971335579845E-6 ); k0x: array[0..10] of extended = (infinity, 0.3678794411714423215955, 0.067667641618306345947, 0.01659568945595464765978, 0.00457890972218354507343, 0.001347589399817093419327, 4.131253627777264038409E-4, 1.302688522220737440005E-4, 4.193282848781397985267E-5, 1.371220045407550549974E-5, 4.539992976248485153559E-6 ); y05x: array[0..10] of extended = (-infinity, -0.979105073187779411997, -0.0948550227282578848964, 0.2349348211023303844977, 0.2493629593212646219752, 0.0828771619936813904809, -0.08954637974475401842893, -0.1433759573238180132626, -0.07003871851786339196898, 0.04357964394070161754787, 0.09869296282843609949355 ); y0x: array[0..10] of extended = (infinity, -0.5403023058681397174009, 0.2080734182735711934988, 0.3299974988668151524239, 0.1634109052159029786598, -0.05673243709264525289333, -0.1600283811083943367576, -0.1077003220490435197345, 0.01818750422607669073361, 0.1012366957649641098187, 0.08390715290764524522589 ); //ベルヌーイ数B(2)、B(4)、B(6)、...、B(20) B : array[1..10] of extended = ( 1.0 / 6, // B(2) -1.0 / 30, 1.0 / 42, -1.0 / 30, 5.0 / 66, -691.0 / 2730, 7.0 / 6, -3617.0 / 510, 43867.0 / 798, -174611.0 / 330); // B(20) m: integer = sizeof(B) div sizeof(extended); //------------------------------------------------------------------------------ // 対数ガンマ関数 function Log_Gamma(x: extended): extended; var i : integer; sum : extended; xx, v : extended; ln_sqrt_2pi: extended; begin ln_sqrt_2pi := ln(sqrt(2 * pi)); // = ln(2 * pi) / 2; sum := 0; v := 1; while x < m do begin v := v * x; x := x + 1; end; xx := 1 / (x * x); for i := m downto 2 do sum := (sum + B[i] / (i * 2 * (2 * i - 1))) * xx; result := (sum + B[1] / 2) / x + ln_sqrt_2pi - ln(v) - x + (x - 0.5) * ln(x); end; // ガンマ関数 function Gamma_Function(x: extended): extended; begin if x < 0 then begin result := pi / (sin(pi * x) * exp(log_Gamma(1 - x))); end else begin result := exp(Log_Gamma(x)); end; end; //------------------------------------------------------------------------------ // ディガンマ関数 整数n >= 0 function Digamma_Function(n: integer): extended; const g= 0.5772156649015328606065120; var s : extended; k : integer; begin if n = 0 then begin result := -infinity; exit; end; if n = 1 then begin result := -g; exit; end; s := 0; for k := 1 to n - 1 do s := s + 1 / k; result := s - g; end; //----------------------- new -------------------------------------------------- // X 値 // v 次数 // flag false 通常 true 変形 function Jvx(v, x: double; flag: boolean): extended; var k : integer; g, j, bkk: extended; function Bk(k : integer): extended; var kn : integer; Bn, Y, Zk: extended; begin Y := -(x / 2) * (x / 2); if flag then Y := -Y; Bn := 1; for kn := 1 to k do begin Zk := Y / (kn * (kn + v)); Bn := Zk * Bn; end; result := Bn end; begin g := Gamma_Function(1 + v); bkk := 0; for k := 0 to 100 do bkk := bkk + Bk(k); j := power(x / 2, v) / g * bkk; result := j; end; // 第1種ベッセル関数 Γ関数使用 // X 値 // v 次数 // flag false 通常 true 変形 function BesselG(X: extended; v: extended; flag: boolean): extended; const MN = 100; var M : Integer; B, S : extended; begin S := 0; for M := 0 to MN do begin B := 1 / Gamma_Function(M + 1) / Gamma_Function(M + v + 1); // B = 1 / (Γ(m + 1)Γ(m + n + 1)) if not flag then S := S + power(-1, M) * B * power(X / 2, 2 * M + v) else S := S + B * power(X / 2, 2 * M + v) end; result := S; end; // 第2種ベッセル関数 // 次数v 非整数 // 値 x function Yax(v, x: extended): extended; begin if Form1.CheckBox2.Checked = true then result := (BesselG(x, v, false) * cos(v * pi) - BesselG(x, -v, false)) / sin(v * pi) else result := (Jvx(v, x, false) * cos(v * pi) - Jvx(-v, x, false)) / sin(v * pi); end; // 第2種ベッセル関数 // X 値 // n 次数 整数 // Mの値は積分上は∞となっていますが精度上50程度で問題ないようです。 function Ynx(x: extended; n: integer): extended; const {$IFDEF CPUX64} M = 50; {$ELSE} M = 100; // 値を大きくするとオーバーフローします。 {$ENDIF} var k : integer; a, b, c: extended; begin if Form1.checkbox2.Checked = true then a := 2 / pi * ln(x / 2) * BesselG(x, n, False) else a := 2 / pi * ln(x / 2) * Jvx(n, x, False); b := 0; for k := 0 to n - 1 do b := b + (Gamma_Function(n - k) / Gamma_Function(k + 1)) * power(x / 2, 2 * k - n); b := b / pi; c := 0; for k := 0 to M do c := c + power(-1, k) * (Digamma_Function(K + 1) + Digamma_Function(k + n + 1)) * power(x / 2, 2 * k + n) / (Gamma_Function(k + 1) * Gamma_Function(n + k + 1)); c := c / pi; result := (a - b - c); end; // 第2種変形ベッセル関数 // 次数v 非整数 // 値 x function Kax(v, x: extended): extended; begin if Form1.checkbox2.Checked = true then result := pi / 2 * ((BesselG(x, -v, true) - BesselG(x, v, true)) / sin(v * pi)) else result := pi / 2 * ((Jvx(-v, x, true) - Jvx(v, x, true)) / sin(v * pi)); end; // 第2種変形ベッセル関数 // X 値 // n 次数 整数 // Mnの値は積分上は∞となっていますが精度上50程度で問題ないようです。 function KnX(x: extended; n: integer): extended; const {$IFDEF CPUX64} MN = 50; {$ELSE} MN = 100; // 値を大きくするとオーバーフローします。 {$ENDIF} var a, b, c: extended; p : integer; begin if Form1.CheckBox2.Checked = True then a := power(-1, n + 1) * BesselG(x, n, true) * ln(x / 2) else a := power(-1, n + 1) * Jvx(n, x, true) * ln(x / 2); b := 0; for p := 0 to Mn do b := b + 1 / Gamma_Function(p + 1) / Gamma_Function(n + p + 1) * power(x / 2, 2 * p) * (Digamma_Function(p + 1) + Digamma_Function(p + n + 1)); b := b * power(- 1, n) / 2 * power(x / 2, n); c := 0; for p := 0 to n - 1 do c := c + power(-1, p) * Gamma_Function(n - p) / Gamma_Function(p + 1) * power(x / 2, 2 * p); c := c / 2 * power(x / 2, -n); result := a + b + c; end; // 第2種ベッセル関数 // v 次数 // x 値 function Yvx(v, x: extended): extended; var n: integer; begin n := round(v); if n = v then begin if n < 0 then n := abs(n); result := Ynx(x, n); if v < 0 then result := result * power(-1, n); end else result := Yax(v, x); end; // 第2種球ベッセル関数グラフ表示 // xmax Xの最大値 // n 次数 procedure TForm1.GraphNnX(xmax, v: extended); const NMAX = 1000; var Y, X: extended; I : integer; XMIN: extended; begin XMIN := 0.001; for I := 0 to NMAX - 1 do begin X := XMIN + (xmax - XMIN) * I / NMAX; Y := sqrt(pi / (2 * x)) * Yvx(v + 0.5, X); if Y > 10000 then Y := 10000; if Y < -10000 then Y := -10000; Series1.AddXY(X, Y); end; end; // 第2種変形ベッセル関数 // v 次数 // x 値 function kvx(v, x: extended): extended; var n: integer; begin n := round(v); if n = v then begin if n < 0 then n := abs(n); result := KnX(x, n); end else result := KaX(v, x); end; // 第2種変形球ベッセル関数グラフ表示 // xmax Xの最大値 // n 次数 procedure TForm1.GraphKnX(xmax, v: extended); const NMAX = 1000; var Y, X: extended; I: integer; XMIN: extended; begin XMIN := 0.001; for I := 1 to NMAX - 1 do begin X := XMIN + (xmax - XMIN) * I / NMAX; Y := sqrt(2 / (pi * x)) * Kvx(v + 0.5, x); if Y > 10000 then Y := 10000; if Y < -10000 then Y := -10000; Series1.AddXY(X, Y); end; end; // 第2種球ベッセル関数計算 procedure TForm1.BitBtn1Click(Sender: TObject); var x, v, y: extended; ch: integer; begin val(Labelededit2.Text, v, ch); if ch <> 0 then begin MessageDlg('次数xに間違いがあります。', mtInformation, [mbOk], 0, mbOk); exit; end; val(Labelededit1.Text, x, ch); if ch <> 0 then begin MessageDlg('値xに間違いがあります。', mtInformation, [mbOk], 0, mbOk); exit; end; if x < 0 then begin MessageDlg('値xは0(ゼロ)以上にして下さい。', mtInformation, [mbOk], 0, mbOk); exit; end; if x > 30 then begin MessageDlg('値xが大きすぎます。', mtInformation, [mbOk], 0, mbOk); exit; end; Series1.Clear; Series2.Clear; memo1.Clear; y := 0; Chart1.Title.Text.Clear; chart1.LeftAxis.Title.Caption := ''; if checkbox1.Checked = false then begin Chart1.Title.Text.Append('第2種球ベッセル関数'); if x > 0 then begin y := sqrt(pi / (2 * x)) * Yvx(v + 0.5, X); if Chart1.LeftAxis.Minimum >= y + 4 then begin Chart1.LeftAxis.Minimum := y - 4; Chart1.LeftAxis.Maximum := y + 4; end else begin Chart1.LeftAxis.Maximum := y + 4; Chart1.LeftAxis.Minimum := y - 4; end; GraphNnX(10, v); memo1.Lines.Add('第2種球ベッセル関数値'); memo1.Lines.Add('yv(x) = ' + floatTostr(y)); end; end else begin Chart1.Title.Text.Append('第2種変形球ベッセル関数'); if x > 0 then begin y := sqrt(2 / (pi * x)) * Kvx(v + 0.5, x); if Chart1.LeftAxis.Minimum >= y + 4 then begin Chart1.LeftAxis.Minimum := y - 1; Chart1.LeftAxis.Maximum := Y + 4; end else begin Chart1.LeftAxis.Maximum := Y + 4; Chart1.LeftAxis.Minimum := y - 1; end; GraphKnX(10, v); memo1.Lines.Add('第2種変形球ベッセル関数値'); memo1.Lines.Add('kv(x) = ' + floatTostr(y)); end; end; if (x > 0) and (abs(y) < 9996) then Series2.AddXY(x, y); if x = 0 then begin if checkbox1.Checked = false then begin memo1.Lines.Add('第2種球ベッセル関数値'); if v > 0 then memo1.Lines.Add('yv(x) = - ∞') else memo1.Lines.Add('yv(x) = ∞'); end else begin memo1.Lines.Add('第2種変形球ベッセル関数値'); memo1.Lines.Add('kv(x) = ∞'); end; end; end; // 計算誤差の確認 procedure TForm1.Button1Click(Sender: TObject); var i, p, l: integer; yk: extended; def : extended; defs, re, ex, ads, no : string; begin memo1.Clear; Series1.Clear; Series2.Clear; chart1.LeftAxis.AutomaticMaximum := true; chart1.LeftAxis.Minimum := 0; {$IFDEF CPUX64} chart1.LeftAxis.Title.Caption := 'xxE16'; Memo1.Lines.Append('X64 double'); {$ELSE} chart1.LeftAxis.Title.Caption := 'xxE16'; Memo1.Lines.Append('X86 extended'); {$ENDIF} for i := 0 to 10 do begin if i <> 0 then begin if checkbox3.Checked = true then begin if checkbox1.Checked = false then begin yk := sqrt(pi / (2 * i)) * Yvx(1, i); def := abs(Y05x[i] - yk); end else begin yk := sqrt(2 / (pi * i)) * kvx(1, i); def := abs(k05x[i] - yk); end; end else begin if checkbox1.Checked = false then begin yk := sqrt(pi / (2 * i)) * Yvx(0.5, i); def := abs(Y0x[i] - yk); end else begin yk := sqrt(2 / (pi * i)) * kvx(0.5, i); def := abs(k0x[i] - yk); end; end; end else def := 0; {$IFDEF CPUX64} Series1.AddXY(i, def * 1E16); {$ELSE} Series1.AddXY(i, def * 1E16); {$ENDIF} if def <> 0 then defs := floatTostr(def) else defs := ' 0'; l := length(defs); p := pos('E', defs, 1); re := copy(defs, 0, 4); ex := copy(defs, p, l - p + 1); no := inttostr(i); if i < 10 then no := ' ' + no; if i mod 2 = 0 then begin ads := no + ' ' + re + ex; if i = 10 then memo1.Lines.Text := memo1.Lines.Text + ads; end else begin ads := ads + ' ' + no + ' ' + re + ex; memo1.Lines.Append(ads); end; end; end; end.
Spherical_Bessel2a_function.zip