2020/02/07
第一二種の楕円積分のmの値がマイナスの時の計算に間違いがあったので修正しました。
計算結果が正しいことは、分割積分で確認しました。
2020/01/07
第二種楕円積分のK = 1の時とK > 1の時の計算にミスが有ったので修正しました。
cn-, dn-について、x = 1&k = 1の時、ゼロにならないのを修正しました。
ヤコビのsn-、cn-、dn-関数
ヤコビのsn-、cn-、dn-関数の計算プログラムです。
Mathematics Source Library C & ASM にあるプログラムをDelphi用に変換したものです。
sn-,cn-,dn-の詳細については ヤコビの楕円関数を参照してください。
sn-の計算は、第一種不完全楕円積分の計算です。
kの値は、積分できる範囲であれば、此処では1を超えても、変換可能です。
パラメタmの場合は、マイナスの値でも可能です。
αの単位はRadでK=sin(α)となり、kの値は1を超えることはありません。
sn-、cn-、dn-の計算をする場合は、値(x)を使用します、第一、二楕円積分の場合は、値(deg)です。
xの値は、1より大きくとることは出来ません、snの値はsin(φ)で1を超えることが無い為です。
degの入力の値に制限はありません。
入力値をdegにしているのは、単に角度を分かりやすくするためです。
第二種不完全積分の計算は、sn-、cn-、dn-の計算には必要ないのですが、参考としてとりあげています。
楕円積分については、此処での計算方式とは違うやり方の方法が既に取り上げてありますが、此処での計算が一番高速で計算出来ます。
第二種楕円積分のランデン変換には、第一種楕円積分も含まれています。
プログラム
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.ExtCtrls; type TForm1 = class(TForm) Memo1: TMemo; GroupBox1: TGroupBox; a_Edit: TLabeledEdit; mRadioButton: TRadioButton; aRadioButton: TRadioButton; m_Edit: TLabeledEdit; K_Edit: TLabeledEdit; kRadioButton: TRadioButton; GroupBox2: TGroupBox; secondBtn: TButton; FirstBtn: TButton; Deg_Edit: TLabeledEdit; GroupBox3: TGroupBox; snukbtn: TButton; x_Edit: TLabeledEdit; procedure snukbtnClick(Sender: TObject); procedure FirstBtnClick(Sender: TObject); procedure secondBtnClick(Sender: TObject); private { Private 宣言 } function datainput(var arg, sarg: char;var x, param: double; FE: boolean): boolean; public { Public 宣言 } end; function Inverse_Jacobi_sn(x: double; arg: char; param: double; var errF: byte): double; function Inverse_Jacobi_cn(x: double; arg: char; param: double; var errF: byte): double; function Inverse_Jacobi_dn(x: double; arg: char; param: double; var errF: byte): double; var Form1: TForm1; implementation {$R *.dfm} uses system.Math, First_kind, second_kind; procedure parameter(arg: char; x: double; var k, m: double); begin case arg of 'a': begin k := sin(x); m := k * k; end; 'm': begin m := x; k := sqrt(abs(m)); end; else begin k := abs(x); m := k * k; end; end; end; function Inverse_Jacobi_cd(x, k: double; var errF: byte): double; var m, phi: double; begin m := k * k; result := 0; if (m > 0) and (m < 1) then begin phi := (1 - m) / (1 - m * x * x); if phi < 0 then begin errF := 3; exit; end; phi := x * sqrt(phi); if abs(phi) > 1 then begin errF := 3; exit; end; phi := arccos(phi); result := Legendre_Elliptic_Integral_First_Kind(phi, 'k', k); exit; end; if m = 0 then begin if abs(x) > 1 then begin errF := 1; exit; end; result := arccos(x); exit; end; end; function Inverse_Jacobi_sd(x, k: double; var errF: byte): double; var m, phi: double; begin result := 0; m := k * k; if (m > 0) and (m < 1) then begin phi := x / sqrt(1 + m * x * x ); if abs(phi) > 1 then begin errf := 3; exit; end; phi := arcsin(phi); result := Legendre_Elliptic_Integral_First_Kind(phi, 'k', k); exit; end; if m = 0 then begin if abs(x) > 1 then begin errF := 1; exit; end; result := arcsin(x) end else begin result := arcsinh(x); end; end; function Inverse_Jacobi_nd(x, k: double; var errF: byte): double; begin result := Inverse_Jacobi_dn(1 / x, 'k', k, errF); end; function Inverse_Jacobi_dn(x: double; arg: char; param: double; var errF: byte): double; var k, m, kp, phi: double; begin result := 0; errF := 0; if x < 0 then begin errF := 5; exit; end; parameter(arg, param, k, m); if (m > 0) and (m < 1) then begin phi := 1 - x * x; if phi < 0 then begin errF := 1; exit; end; phi := sqrt(phi) / abs(k); if phi > 1 then begin errF := 2; exit; end; phi := arcsin(phi); result := Legendre_Elliptic_Integral_First_Kind(phi, 'k', k); exit; end; if m = 0 then begin result := 0; exit; end; if m = 1 then begin if abs(x) > 1 then begin errF := 1; exit; end; result := arcsech(x); exit; end; if m > 1 then begin result := Inverse_Jacobi_cn(x, 'k', 1 / k, errF) / k; exit; end; kp := sqrt(1 - m); result := Inverse_Jacobi_nd(x, k / kp, errF) / kp; if errF = 1 then errF := 4; if errF = 2 then errF := 3; end; function Inverse_Jacobi_sn(x: double; arg: char; param: double; var errF: byte): double; var k, m, kp, phi : double; begin errF := 0; result := 0; parameter(arg, param, k, m); if (m > 0) and (m < 1) then begin if abs(x) > 1 then begin errF := 1; exit; end; phi := arcsin(x); result := Legendre_Elliptic_Integral_First_Kind(phi, 'k', k); exit; end; if (m = 0) then begin if abs(x) > 1 then begin errF := 1; exit; end; result := arcsin(x); exit; end; if m = 1 then if x = -1 then begin result := -infinity; exit; end else if x = 1 then begin result := infinity; exit; end else begin if abs(x) > 1 then begin errF := 1; exit; end; result := arctanh(x); exit; end; if m > 1 then begin result := Inverse_Jacobi_sn(k * x, 'k', 1 / k, errF) / k; exit; end; kp := sqrt(1 - m); result := Inverse_Jacobi_sd(kp * x, k / kp, errF) / kp; end; function Inverse_Jacobi_cn(x: double; arg: char; param: double; var errF: byte): double; var k, m, kp, phi: double; begin errF := 0; result := 0; if x < 0 then begin errF := 5; exit; end; parameter(arg, param, k, m); if (m > 0) and (m < 1) then begin if abs(x) > 1 then begin errF := 1; exit; end; phi := arccos(x); result := Legendre_Elliptic_Integral_First_Kind(phi, 'k', k); exit; end; if m = 0 then begin if abs(x) > 1 then begin errF := 1; exit; end; result := arccos(x); exit; end; if m = 1 then begin if abs(x) > 1 then begin errF := 1; exit; end; result := arcsech(x); exit; end; if m > 1 then begin result := Inverse_Jacobi_dn(x, 'k', 1.0 / k, errF) / k; exit; end; kp := sqrt(1 - m); result := Inverse_Jacobi_cd(x, k / kp, errF) / kp; end; function TForm1.datainput(var arg, sarg: char; var x, param: double; FE: boolean): boolean; var ch : integer; begin result := false; arg := '0'; sarg := ' '; if kradiobutton.Checked then begin arg := 'k'; sarg := 'k'; end; if mradiobutton.Checked then begin arg := 'm'; sarg := 'm'; end; if aradiobutton.Checked then begin arg := 'a'; sarg := 'α'; end; if FE then val(Deg_edit.text, x, ch) else val(x_edit.text, x, ch); if ch <> 0 then begin application.MessageBox('Xの値に間違いがあります。','注意',0); exit; end; param := 0; if kradiobutton.Checked then begin val(k_edit.Text, param, ch); if ch <> 0 then begin application.MessageBox('離心率(k)に間違いがあります。','注意', 0); exit; end; end; if mradiobutton.Checked then begin val(m_edit.Text, param, ch); if ch <> 0 then begin application.MessageBox('パラメーター(m)に間違いがあります。','注意', 0); exit; end; end; if aradiobutton.Checked then begin val(a_edit.Text, param, ch); if ch <> 0 then begin application.MessageBox('Radian(α)に間違いがあります。','注意', 0); exit; end; end; result := true; end; procedure TForm1.snukbtnClick(Sender: TObject); var x, param, snmu: double; cnmu, dnmu: double; arg, sarg: char; errF: byte; // 0: ok 1: x の値大 2: x or k,m 小 3 x 大 or m小 4 x 小 begin if not datainput(arg, sarg, x, param, False) then exit; memo1.Clear; snmu := Inverse_Jacobi_sn(x, arg, param, errF); case errF of 1 : memo1.Lines.Add('sn-(x,' + sarg +')= xの値大'); 3 : memo1.Lines.Add('sn-(x,' + sarg +')= xの値大 又は' + sarg + 'の絶対値が小'); 0 : memo1.Lines.Add('sn-(x,' + sarg +')= ' + floatTostr(snmu)); end; memo1.Lines.Add(''); cnmu := Inverse_Jacobi_cn(x, arg, param, errF); case errF of 1 : memo1.Lines.Add('cn-(x,' + sarg +')= xの値大'); 2 : memo1.Lines.Add('cn-(x,' + sarg +')= xの値 又は' + sarg + 'の絶対値小'); 3 : memo1.Lines.Add('cn-(x,' + sarg +')= xの値大 又は' + sarg + 'の絶対値が小'); 5 : memo1.Lines.Add('cn-(x,' + sarg +')= xの値にマイナスはありません。'); 0 : memo1.Lines.Add('cn-(x,' + sarg +')= ' + floatTostr(cnmu)); end; memo1.Lines.Add(''); dnmu := Inverse_Jacobi_dn(x, arg, param, errF); case errF of 1 : memo1.Lines.Add('dn-(x,' + sarg +')= xの値大'); 2 : memo1.Lines.Add('dn-(x,' + sarg +')= xの値 又は' + sarg + 'の絶対値小'); 3 : memo1.Lines.Add('dn-(x,' + sarg +')= xの値大 又は' + sarg + 'の絶対値が小'); 4 : memo1.Lines.Add('dn-(x,' + sarg +')= xの値小 又は' + sarg + 'の絶対値が小'); 5 : memo1.Lines.Add('dn-(x,' + sarg +')= xの値にマイナスはありません。'); 0 : memo1.Lines.Add('dn-(x,' + sarg +')= ' + floatTostr(dnmu)); end; end; procedure TForm1.FirstBtnClick(Sender: TObject); var x, F : double; param, k, m: double; arg, sarg: char; begin if not datainput(arg, sarg, x, param, True) then exit; memo1.Clear; memo1.Lines.Add('第一種楕円積分'); parameter(arg, param, k, m); if m > 1 then begin F := arcsin(sqrt(1 / m)); F := F / pi * 180; memo1.Lines.Add('積分角度の上限=' + floattostr(F)); if abs(x) > F then begin memo1.Lines.Add('積分角度の範囲を超えています'); exit; end; end; x := x / 180 * pi; F := Legendre_Elliptic_Integral_First_Kind(x, arg, param); memo1.Lines.Add('F(x,' + sarg +')= ' + floatTostr(F)); end; procedure TForm1.secondBtnClick(Sender: TObject); var x, E : double; param, k, m: double; arg, sarg: char; begin if not datainput(arg, sarg, x, param, True) then exit; memo1.Clear; memo1.Lines.Add('第二種楕円積分'); parameter(arg, param, k, m); if m > 1 then begin E := arcsin(sqrt(1 / m)); E := E / pi * 180; memo1.Lines.Add('積分角度の上限=' + floattostr(E)); if abs(x) > E then begin memo1.Lines.Add('積分角度の範囲を超えています'); exit; end; end; x := x / 180 * pi; E := Legendre_Elliptic_Integral_Second_Kind(x, arg, param); memo1.Lines.Add('E(x,' + sarg +')= ' + floatTostr(E)); end; end.
unit First_kind; interface function Legendre_Elliptic_Integral_First_Kind(amplitude: double; arg: char; x: double): double; implementation uses system.Math, main, System.SysUtils; const PI_2 = pi / 2; // ランデン変換第一種積分 procedure Landen_Transform(phi, parameter: double; var K, F: double); const EPSILON = 1E-12; var two_n : double; a, g : double; a_old : double; g_old : double; tan_phi : double; N : integer; begin N := 0; two_n := 1; a := 1; g := sqrt(1.0 - parameter); while true do begin tan_phi := tan(phi); two_n := two_n + two_n; phi := phi + phi - arctan((a - g) * tan_phi / (a + g * tan_phi * tan_phi)); g_old := g; a_old := a; a := (g_old + a_old) / 2; g := sqrt(g_old * a_old); inc(N); if abs(a_old - g_old) <= (a_old * EPSILON) then break; // if a_old - g_old = 0 then break; end; phi := phi / two_n; F := phi / g; K := PI_2 / g; Form1.Memo1.Lines.Add('Landen Loop=' + intTostr(N)); end; // mの値が1を超える場合の積分処理 function Large_Modulus(amplitude, k : double): double; var F, Kl : double; phi : double; sin_phi: double; n : integer; begin phi := amplitude; n := trunc((phi + PI_2) / PI); phi := phi - n * PI; n := n + n; sin_phi := sin(phi); if abs(sin_phi) >= 1 / k then begin if phi > 0 then phi := PI_2 else phi := -PI_2; end else phi := arcsin(k * sin_phi); Landen_Transform(abs(phi), 1 / (k * k) , Kl, F); if phi >= 0 then F := F + n * Kl else F := n * Kl - F; result := F / k; end; // 第一種楕円積分処理 積分範囲を制限なしに拡大します // K 完全積分 // F 不完全積分 // phi rad単位 procedure Elliptic_Integral_First_Kind(phi, m: double; var K, F: double); var n : integer; begin n := trunc((phi + PI_2) / PI); phi := phi - n * PI; n := n + n; Landen_Transform(abs(phi), m, K, F); if phi >= 0 then F := F + n * K else F := n * K - F; end; // ルジャンドルの第一種楕円積分 function Legendre_Elliptic_Integral_First_Kind(amplitude: double; arg: char; x: double):double; var phi : double; k, m : double; Kl, F : double; sgn_amplitude : integer; begin sgn_amplitude := sign(amplitude); if amplitude = 0 then begin result := 0; exit; end; if x = 0 then begin result := sgn_amplitude; exit; end; case arg of 'k': begin k := abs(x); m := k * k; end; 'm': begin m := x; k := sqrt(abs(m)); end; 'a': begin k := abs(sin(x)); m := k * k; end; else begin k := abs(x); m := k * k; end; end; if (m > 0) and (m < 1) then begin Elliptic_Integral_First_Kind(amplitude, m, Kl, F); result := F; exit; end; if m < 0 then begin phi := abs(amplitude); Elliptic_Integral_First_Kind(abs(PI_2 - phi), abs(m / (1 - m)), Kl, F); // phi := PI_2 - abs(amplitude); // Elliptic_Integral_First_Kind(abs(phi), abs(m / (1 - m)), Kl, F); if phi > PI_2 then begin result := sgn_amplitude * (Kl + F) / sqrt(1 - m); exit; end else begin result := sgn_amplitude * (Kl - F) / sqrt(1 - m); exit; end; end; if m = 1 then begin if abs(amplitude) >= PI_2 then begin result := sgn_amplitude * Infinity; exit; end; x := tan(amplitude); result := sgn_amplitude * (ln(abs(x) + sqrt(1 + x * x))); exit; end; // case m > 1 result := Large_Modulus(amplitude, k); end; end.
unit second_kind; interface function Legendre_Elliptic_Integral_Second_Kind(amplitude: double; arg: char; x: double): double; implementation uses System.math, main, System.SysUtils; const PI_4 = PI / 4; PI_2 = PI / 2; // ランデン変換第一種二種積分 procedure Landen_Transform(phi, parameter: double; var F, Fk, E, Ek: double); const EPSILON = 1E-12; var two_n : double; a : double; g : double; a_old : double; g_old : double; tan_phi : double; sum : double; integral: double; N : integer; begin two_n := 1; g := sqrt(1 - parameter); a := 1; sum := 2 * (2 - parameter); integral := 0; N := 0; while true do begin tan_phi := tan(two_n * phi); sum := sum - two_n * (a - g) * (a - g); two_n := two_n + two_n; phi := phi - arctan((a - g) * tan_phi / (a + g * tan_phi * tan_phi) ) / two_n; integral := integral + (a - g) * sin(two_n * phi); g_old := g; a_old := a; a := (g_old + a_old) / 2; g := sqrt(g_old * a_old); inc(N); if abs(a_old - g_old) <= (a_old * EPSILON) then break; // if a_old - g_old = 0 then break; end; F := phi / g; Fk := PI_2 / g; E := integral / 2 + sum / 4 * phi / g; Ek := PI_4 / a * sum / 2; Form1.Memo1.Lines.Add('Landen Loop=' + intTostr(N)); end; // mの値が1を超える場合の積分処理 function Large_Modulus(amplitude, kp: double): double; var F, K, E, Ek : double; phi : double; sin_phi : double; n : integer; begin phi := amplitude; n := Trunc((phi + PI_2) / PI); phi := phi - n * PI; n := n + n; sin_phi := sin(phi); if abs(sin_phi) >= 1 / kp then begin if phi > 0 then phi := PI_2 else phi := -PI_2; end else phi := arcsin(kp * sin_phi); Landen_Transform(abs(phi), 1 / (kp * kp) , F, K, E, Ek); Ek := kp * Ek + (1 - kp * kp) * K / kp; E := kp * E + (1 - kp * kp) * F / kp; if phi >= 0 then E := E + n * Ek else E := n * Ek - E; result := E; end; // 第二種楕円積分処理 積分範囲を制限なしに拡大します // Em 完全積分 // Em_phi 不完全積分 // phi rad単位 procedure Elliptic_Integral_Second_Kind(phi, m: double; var Em, Em_phi: double); var F, Fk : double; n : integer; begin n := trunc((phi + PI_2 )/ PI); phi := phi - n * PI; n := n + n; Landen_Transform(abs(phi), m, F, Fk, Em_phi, Em); if phi >= 0 then Em_phi := Em_phi + n * Em else Em_phi := n * Em - Em_phi; end; // ルジャンドルの第二種楕円積分 function Legendre_Elliptic_Integral_Second_Kind(amplitude: double; arg: char; x: double): double; var phi : double; k, m : double; Em, Em_phi : double; n : integer; sgn_amplitude : integer; begin sgn_amplitude := sign(amplitude); if amplitude = 0 then begin result := 0; exit; end; if x = 0 then begin result := amplitude; exit; end; case arg of 'k': begin k := abs(x); m := k * k; end; 'm': begin m := x; k := sqrt(abs(m)); end; 'a': begin k := sin(x); m := k * k; end; else begin k := abs(x); m := k * k; end; end; if (m > 0) and (m < 1) then begin Elliptic_Integral_Second_Kind(amplitude, m, Em, Em_phi); result := Em_phi; exit; end; if m < 0 then begin phi := abs(amplitude); Elliptic_Integral_Second_Kind(abs(PI_2 - phi), abs(m / (1 - m)), Em, Em_phi); // phi := PI_2 - abs(amplitude); // Elliptic_Integral_Second_Kind(abs(phi), abs(m / (1 - m)), Em, Em_phi); if phi > PI_2 then result := sgn_amplitude * (Em + Em_phi) * sqrt(1 - m) else result := sgn_amplitude * (Em - Em_phi) * sqrt(1 - m); exit; end; if m = 1 then begin phi := abs(amplitude); if phi <= PI_2 then begin result := sin(amplitude); exit; end; n := trunc((phi + PI_2) / PI); n := n + n; result := sgn_amplitude * (n + sin(phi - n * PI)); exit; end; // case m > 1 result := Large_Modulus(amplitude, k); end; end.