楕円積分の逆変換

 第一、二、三種楕円積分の逆変換について検討してみました。
ヤコビの楕円関数SNがあるのですか、これは、第一種楕円積分の逆変換と同じになっているのですが、値の範囲は、-1~1の範囲に限られます。
ルジャンドルの楕円積分計算であると、積分の範囲に制限はないので、これの逆変換です。

 積分の範囲が±π/2の範囲を超えてしまっていて、微分できる計算になっていないようなので、ニュートン法の使用が難しく、適当な仮の値を与えて、積分を行い、値が小さかったら大きくし、大きかったら小さくするような計算方法をとったので計算に時間が掛かっています。
その代わり積分の計算に間違いがない限り、逆変換の値も正しい値を求めることが出来ます。

 最初に角度、離心率、第三種楕円積分の場合は、特性値も入力し、積分計算を行います。
計算を行うと、一番左側の枠に積分値が表示されます。
一番右側のarc計算ボタンをクリックすると、逆計算が行われ右側の表示枠にその結果が表示されます。
 一番左の枠に適当な値をいれて、arc計算ボタンをクリックすると、指定された、離心率、特性値を利用して逆計算が行われます。
一番左の枠に値を入れる場合、値の大きさの制限はしていないので、逆計算した結果が360°以上になる場合もあります。
 ループ数の表示は、逆計算のため、積分計算が実行された回数です。
ニュートン法が使用できれば数ループで収束するのですが、使用出来ないため、ループ数が多くなっています。
 第一種楕円積分の逆変換は、ヤコビの楕円関数SNなので、SNを使用した、縄跳び紐の追加してみました。
 SNの計算は、計算速度を早くするため、am(Φ)による計算にしました。
縄跳び紐の支持距離、離心率、左端からの距離を入力して紐決算ボタンをクリックすると、縄跳び紐のグラフと、紐の中心高さ、長さが表示されます。
左端からの距離による紐の長さの計算は、単純に計算できないので、分割積分計算が行われます。



プログラム

//-----------------------------------------------------
// 第一種第二種第三種楕円積分のプログラム
//-----------------------------------------------------
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, VclTee.TeeGDIPlus,
  VCLTee.TeEngine, VCLTee.Series, VCLTee.TeeProcs, VCLTee.Chart;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Label1: TLabel;
    LabeledEdit1: TLabeledEdit;
    LabeledEdit2: TLabeledEdit;
    LabeledEdit3: TLabeledEdit;
    third_btn: TButton;
    Edit3: TEdit;
    Label4: TLabel;
    Edit5: TEdit;
    Label5: TLabel;
    Label6: TLabel;
    first_Btn: TButton;
    second_Btn: TButton;
    LabeledEdit4: TLabeledEdit;
    jacobi_sn_btn: TButton;
    LabeledEdit5: TLabeledEdit;
    Label2: TLabel;
    LabeledEdit6: TLabeledEdit;
    LabeledEdit7: TLabeledEdit;
    arcE_btn: TButton;
    LabeledEdit8: TLabeledEdit;
    LabeledEdit9: TLabeledEdit;
    arc_pi_btn: TButton;
    Label3: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    LabeledEdit10: TLabeledEdit;
    LabeledEdit11: TLabeledEdit;
    Memo1: TMemo;
    nawaBtn: TButton;
    Chart1: TChart;
    Series1: TLineSeries;
    Series2: TPointSeries;
    LabeledEdit12: TLabeledEdit;
    procedure third_btnClick(Sender: TObject);
    procedure first_BtnClick(Sender: TObject);
    procedure second_BtnClick(Sender: TObject);
    procedure jacobi_sn_btnClick(Sender: TObject);
    procedure arcE_btnClick(Sender: TObject);
    procedure arc_pi_btnClick(Sender: TObject);
    procedure nawaBtnClick(Sender: TObject);
  private
    { Private 宣言 }
    function RJ(x1, y1, z1, p1: double): double;
    function RF(x1, y1, z1: double): double;
    function datainput(var k, deg: double): boolean;
    function calc_first_elliptic_integral(k, deg: double; var ALFRG : byte): double;
    function calc_second_elliptic_integral(k, deg: double; var ALFRG : byte): double;
    function calc_Third_elliptic_integral(a, k, deg: double; var ALFRG : byte): double;
    function R_jacobi_sn(u, k: double): double;
    function arcE(u, k: double): double;
    function arc_pi(a, u, k: double): double;
    function Jacobi_sn(u, k: double): double;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Math;


// Carlson's Elliptic Integral RJ
function TForm1.RJ(x1, y1, z1, p1: double): double;
var
  s, fa, rj, rk   : double;
  la, mu          : double;
  al, be          : double;
  r1, a1, b1      : double;
  a2, b2, mv, s1  : double;
  rc, r0          : double;
  s12, s13, s14   : double;
  s15, s16        : double;
  x2, y2, z2      : double;
  x3, y3, z3      : double;
  s2, s3, p2, p3  : double;
  s4, s5          : double;
  x31, y31, z31, p31 : double;
  s22, s23, s2s3  : double;

  procedure rcab;
  begin
    rc := 1;
    a1 := al;
    b1 := be;
    repeat
      r1 := rc;
      la := 2 * sqrt(a1 * b1) + b1;
      a2 := (a1 + la) / 4;
      b2 := (b1 + la) / 4;
      mv := (a1 + 2 * b1) / 3;
      s1 := (b1 - a1) / (3 * mv);
      s12 := s1 * s1;
      s13 := s12 * s1;
      s14 := s13 * s1;
      s15 := s14 * s1;
      s16 := s15 * s1;
      rc := (1 + 3 * s12 / 10 + s13 / 7 + 3 * s14 / 8 + 9 * s15 / 22 + 159 * s16 / 208) / sqrt(mv);
      a1 := a2;
      b1 := b2;
    until r1 = rc;
  end;

  function ss: double;
  begin
    x31 := x31 * x3;
    y31 := y31 * y3;
    z31 := z31 * z3;
    p31 := p31 * p3;
    result := (x31 + y31 + z31 + p31);
  end;

begin
  s := 0;
  fa := 3;
  rj := 0;
  repeat
    r0 := rj;
    la := sqrt(x1 * y1) + sqrt(x1 * z1) + sqrt(y1 * z1);
    mu := (x1 + y1 + z1 + 2 * p1) / 5;
    x2 := (x1 + la) / 4;
    y2 := (y1 + la) / 4;
    z2 := (z1 + la) / 4;
    p2 := (p1 + la) / 4;
    x3 := 1 - x1 / mu;
    y3 := 1 - y1 / mu;
    z3 := 1 - z1 / mu;
    p3 := 1 - p1 / mu;
    x31 := x3;
    y31 := y3;
    z31 := z3;
    p31 := 2 * p3;
    s2 := ss / 4;
    s3 := ss / 6;
    s4 := ss / 8;
    s5 := ss / 10;
    al := (sqrt(x1) + sqrt(y1) + sqrt(z1)) * p1 + sqrt(x1 * y1 * z1);
    al := al * al;
    be := p1 * (p1 + la) * (p1 + la);
    rcab;
    s22 := 3 * s2 * s2 / 22;
    s23 := s2 * s2 * s2 / 10;
    s2s3 := s2 * s3 * 3 / 13;
    s := s + fa * rc;
    rk := 1 + 3 * s2 / 7 + s3 / 3 + s22 + s2s3 + 3 * s4 / 11 + 3 * s5 / 13 - s23;
    fa := fa / 4;
    mu := sqrt(1 / (mu * mu * mu));
    rj := s + fa / 4 * (rk + 3 * s3 * s3 / 10 + 3 * s2 * s4 / 5) * mu;
    x1 := x2;
    y1 := y2;
    z1 := z2;
    p1 := p2;
  until rj = r0;
  result := rj;
end;

// Carlson's Elliptic Integral RF
function TForm1.RF(x1, y1, z1: double): double;
var
  la, mu      : double;
  x2, y2, z2  : double;
  x3, y3, z3  : double;
  s2, s3      : double;
  r0, rf      : double;
  s22, s23, s2s3, s32 : double;
begin
  rf := 0;
  repeat
    r0 := rf;
    la := sqrt(x1 * Y1) + sqrt(x1 * z1) + sqrt(y1 * z1);
    mu := (x1 + y1 + z1) / 3;
    x2 := (x1 + la) / 4;
    y2 := (y1 + la) / 4;
    z2 := (z1 + la) / 4;
    x3 := 1 - x2 / mu;
    y3 := 1 - y2 / mu;
    z3 := 1 - z2 / mu;
    s2 := (x3 * x3 + y3 * y3 + z3 * z3) / 4;
    s3 := (x3 * x3 * x3 + y3 * y3 * y3 + z3 * z3 * z3) / 6;
    s22 := s2 * s2 / 6;
    s23 := 5 * s2 * s2 * s2 / 26;
    s32 := 3 * s3 * s3 / 26;
    s2s3 := s2 * s3 * 3 / 11;
    rf := (1 + s2 / 5 + s3 / 7 + s22 + s2s3 + s23 + s32) / sqrt(mu);
    x1 := x2;
    y1 := y2;
    z1 := z2;
  until rf = r0;
  result := rf;
end;

// 入力処理
function TForm1.datainput(var k, deg: double): boolean;
var
  ch, J       : integer;
  eqd         : double;
  absdeg      : double;
  degmax, kmax: double;
begin
  result := False;
  val(LabeledEdit2.Text, deg, ch);
  if ch <> 0 then begin
    application.MessageBox('φ(deg)の値に間違いがあります。','φ(deg)',0);
    exit;
  end;
// 計算上角度の上限下限はありませんがが一応+-360°で限定
  if abs(deg) > 360 then begin
    application.MessageBox('360≧φ(deg)≧-360の値が範囲外です。','φ(deg)',0);
    exit;
  end;
  val(LabeledEdit3.Text, k, ch);
  if ch <> 0 then begin
    application.MessageBox('k(離心率)の値に間違いがあります。','k(離心率)',0);
    exit;
  end;
  if (k > 1) or (k < -1) then begin
    degmax := arcsin(sqrt(1 / (k * k))) / pi * 180;
    kmax := sin(deg / 180 * pi);
    kmax := sqrt(1 / (kmax * kmax));
    if deg > degmax then begin
      application.MessageBox(pchar('角度の最大値は' + floatTostr(degmax) + #13#10
       + '又は離心率の最大値は' + floatTostr(kmax) + 'です。'),'k(離心率)',0);
//      exit;
    end;
  end;
  absdeg := abs(deg);
  J := trunc(absdeg / 90);
  if j mod 2 <> 0 then                          // 第2,第4象限の積分角度
    eqd := (J + 1) * 90 - absdeg
  else                                          // 第1,第3象限の積分角度
    eqd := absdeg - (90 * J);
  if j mod 2 <> 0 then                          // 第2,第4象限の積分量
    Label2.Caption := 'sin(φ)= ' + intTostr(J + 1) + '×sin(90°) -' +
                      'sin(' + floatTostrF(eqd, fffixed, 5, 3) + '°)'
  else                                          // 第1,第3象限の積分量
    Label2.Caption := 'sin(φ)= ' + intTostr(J) + '×sin(90°) +' +
                      'sin(' + floatTostrF(eqd, ffFixed, 5, 3) + '°)';
  result := true;
end;

// 第一種楕円積分 ルーチン
function TForm1.calc_first_elliptic_integral(k, deg: double; var ALFRG : byte): double;
var
  J             : integer;
  s, s2         : double;
  c2            : double;
  rf0           : double;
  k2            : double;
  q, eq, eqd    : double;
  fqk, fek      : double;
  fqk90         : double;
  qb            : double;
begin
  ALFRG := 0;
//  fqk := 0;
  qb := deg / 180 * pi;                         // rad単位
  deg := abs(deg);
  q := abs(qb);
  J := trunc(deg / 90);                         // 90°分割数
  if j > 0 then begin                           // 90°以上だったら
    s2 := 1;                                    // sin^2(90°)
  end
  else begin                                    // 90°以下なら
    s  := sin(q);                               // sin(φ)
    s2 := s * s;                                // sin~2(φ)
  end;
  if j mod 2 <> 0 then                          // 第2,第4象限の積分角度
    eqd := (J + 1) * 90 - deg
  else                                          // 第1,第3象限の積分角度
    eqd := deg - (90 * J);
  eq := eqd / 180 * pi;
  k2 := k * k;                                  // k^2
  if ((1 - k2 * s2) <= 0) then begin            // 1-K^2*sin^2(φ) = 0  K=1 φ=π/2 なら
    fqk := MAXDouble;
    ALFRG := 1;
  end
  else begin                                    // 1-K^2*sin^2(φ) > 0 なら
    fqk90 := 0;
    if J > 0 then begin                         // 90°以上だったら
      rf0 := RF(0, 1 - k2, 1);                  // 90°のRF計算
      fqk90 := rf0;                             // rf0 * sin90°= rf0 * 1 = F(90°,K)
    end;
    c2 := cos(eq) * cos(eq);                    // cos^2(φ')
    s  := sin(eq);                              // sin(φ')
    s2 := s * s;                                // sin^2(φ')
    rf0 := RF(c2, 1 - k2 * s2, 1);
    fek := s * rf0;                             // F(φ',K) = rf0 * sin(φ')
    if j mod 2 <> 0 then                        // 第2,第4象限の積分値
      fqk := (j + 1) * fqk90 - fek
    else                                        // 第1,第3象限の積分値
      fqk := j * fqk90 + fek;
    if qb < 0 then fqk := - fqk;                // 積分範囲の符号で積分値の符号設定
  end;
  result := fqk;
end;

// 第一種楕円積分
procedure TForm1.first_BtnClick(Sender: TObject);
var
  ALFRG : byte;
  k, deg: double;
  fqk          : double;
begin
  if not datainput(k, deg) then exit;
  first_Btn.Enabled := false;
  fqk := calc_first_elliptic_integral(k, deg, ALFRG);
  case ALFRG of
    0: Edit5.Text := floatTostr(fqk);
    1: Edit5.Text := '∞';
  end;
  first_Btn.Enabled := true;
end;

// 第二種楕円積分 ルーチン
function TForm1.calc_second_elliptic_integral(k, deg: double; var ALFRG : byte): double;
label
  NO2;
var
  J             : integer;
  s, s2, s3     : double;
  c2            : double;
  rf0           : double;
  k2            : double;
  eq, eqd       : double;
  eqk90         : double;
  rd0           : double;
  eqk, eek      : double;
  qb            : double;
begin
  ALFRG := 0;
  qb := deg;
  deg := abs(deg);
  J := trunc(deg / 90);
  if j mod 2 <> 0 then                          // 第2,第4象限の積分角度
    eqd := (J + 1) * 90 - deg
  else                                          // 第1,第3象限の積分角度
    eqd := deg - (90 * J);
  eq := eqd / 180 * pi;
  k2 := k * k;                                  // k^2
  eqk90 := 0;
  if J > 0 then begin                           // 積分範囲90度より大きかったら
    if (deg = 90 * J) and (k2 = 1) then begin
      eqk := J;
      goto NO2;
    end
    else
      if k2 > 1 then begin
        eqk90 := 1;
        ALFRG := 1;
      end
      else begin                                  // 90°の積分値計算
        if k2 = 1 then eqk90 := 1
        else begin
          rf0 := RF(0, 1 - k2, 1);
          rd0 := RJ(0, 1 - k2, 1, 1);
          eqk90 := rf0 - k2 / 3 * rd0;            // rf0 - k^2 / 3 * RD
        end;
      end;
  end;
  s  := sin(eq);
  s2 := s * s;
  if (eqd < 90) or (k2 * s2 < 1) then begin
    //  if (eqd < 90) or (abs(K) < 1) then begin
    if k2 * s2 < 1 Then begin
    //    if abs(K) < 1 Then begin
      c2 := cos(eq) * cos(eq);
      s3 := s2 * s;
      rf0 := RF(c2, 1 - k2 * s2, 1);
      rd0 := RJ(c2, 1 - k2 * s2, 1, 1);
      eek := s * rf0 - k2 / 3 * s3 * rd0;
    end
    else begin
      eek := 1;
      ALFRG := 1;
    end;
  end
  else begin
    eek := 1;
    ALFRG := 1;
  end;
  if j mod 2 <> 0 then begin                    // 第2,第4象限の積分値
    eqk := (J + 1) * eqk90 - eek;
  end
  else begin                                    // 第1,第3象限の積分値
    eqk := J * eqk90 + eek;
  end;
NO2:
  if qb < 0 then eqk := -eqk;
  result := eqk;
end;

// 第二種楕円積分
procedure TForm1.second_BtnClick(Sender: TObject);
var
  k, deg  : double;
  eqk     : double;
  ALFRG   : byte;
begin
  if not datainput(k, deg) then exit;
  second_Btn.Enabled := false;
  eqk := calc_second_elliptic_integral(k, deg, ALFRG);
  case ALFRG of
    0: Edit3.Text := floatTostr(eqk);    // 第二種
    1: Edit3.Text := 'k(離心率)の値が大きすぎます。'
  end;
  second_Btn.Enabled := true;
end;

// 第三種楕円積分 ルーチン
function TForm1.calc_Third_elliptic_integral(a, k, deg : double; var ALFRG : byte): double;
var
  J             : integer;
  s, s2, s3     : double;
  c2            : double;
  rf0, rj0      : double;
  k2            : double;
  afk, aek      : double;
  afk90, qb     : double;
  q, eq, eqd    : double;
  fek           : double;
begin
  ALFRG := 0;
//  afk := 0;
  qb := deg / 180 * pi;
  q := abs(qb);
  deg := abs(deg);
  J := trunc(deg / 90);
  if j > 0 then begin                             // 90°以上だったら
    s2 := 1;                                      // sin^2(90°)
  end
  else begin                                      // 90°以下なら
    s  := sin(q);                                 // sin(φ)
    s2 := s * s;                                  // sin~2(φ)
  end;
  if j mod 2 <> 0 then                            // 第2,第4象限の積分角度
    eqd := (J + 1) * 90 - deg
  else                                            // 第1,第3象限の積分角度
    eqd := deg - (90 * J);
  eq := eqd / 180 * pi;
  k2 := k * k;                                    // k^2

  if ((1 - k2 * s2) <= 0) then begin              // 1-K^2*sin^2(φ) = 0  K=1 φ=π/2 なら
    ALFRG := 1;
    afk := MaxDouble;
    if (1 - a * s2) < 0 then ALFRG := 2
                        else ALFRG := 1;
  end
  else begin                                      // 1-K^2*sin^2(φ) > 0 なら
    if (1 - a * s2) <= 0 then begin               // aの値が大きすぎたら
      afk := MaxDouble;
      if (1 - a * s2) = 0 then ALFRG := 1         // 1-a^2*sin^2(φ) がゼロのフラグセット
                          else ALFRG := 2;        // 1-a^2*sin^2(φ) がマイナスフラグセット
    end
    else begin
      afk90 := 0;
      if J > 0 then begin                         // 90°以上だったら
        rf0 := RF(0, 1 - k2, 1);                  // 90°のRF計算
        rj0 := RJ(0, 1 - k2, 1, 1 - a * 1);       // 90°のRj計算
        afk90 := rf0 + a / 3 * rj0;               // Π(a, 90°,K)
      end;
      c2 := cos(eq) * cos(eq);                    // cos^2(φ')
      s  := sin(eq);                              // sin(φ')
      s2 := s * s;                                // sin^2(φ')
      s3 := s2 * s;                               // sin^2(φ')
      rf0 := RF(c2, 1 - k2 * s2, 1);
      rj0 := RJ(c2, 1 - k2 * s2, 1, 1 - a * s2);
      fek := s * rf0;                             // F(φ',K) = rf0 * sin(φ')
      aek := fek + a / 3 * s3 * rj0;              // Π(a,φ',K)
      if j mod 2 <> 0 then begin                  // 第2,第4象限の積分値
        afk := (j + 1) * afk90 - aek;
      end
      else begin                                  // 第1,第3象限の積分値
        afk := j * afk90 + aek;
      end;
      if qb < 0 then begin                        // 積分範囲の符号で積分値の符号設定
        afk := - afk;
      end;
    end;
  end;
  result := afk;
end;

// 第三種楕円積分
procedure TForm1.third_btnClick(Sender: TObject);
var
  ALFRG     : byte;
  a, k, deg : double;
  afk, alfa : double;
  ch        : integer;
  absdeg    : double;
begin
  if not datainput(k, deg) then exit;
  val(LabeledEdit1.Text, a, ch);
  if ch <> 0 then begin
    application.MessageBox('a(特性)の値に間違いがあります。','a(特性)',0);
    exit;
  end;
  third_btn.Enabled := false;
  absdeg := abs(deg);
  if absdeg > 90 then alfa := 1
                 else alfa := sin(absdeg / 180 * pi);
  if alfa > 0 then begin
    alfa := 1 / alfa / alfa;
    label6.Caption := 'α(特性n)の上限値= ' + floatTostr(alfa);
  end
  else
    label6.Caption := 'α(特性n)の上限値= ∞';

  afk := calc_Third_elliptic_integral(a, k, deg, ALFRG);
  case ALFRG of
    0 : Edit1.Text := floatTostr(afk);        // 第三種
    1 : Edit1.Text := '∞';                   // 演算誤差により発生率低い
    2 : Edit1.Text := 'a(特性n)の値が大きすぎ'
  end;
  third_btn.Enabled := true;
end;

// 第一種楕円積分逆変換ルーチン
// 此処ではdeg単位で計算します
// k=1 で90度に近い場合非常に大きな値となり精度Doubleでは扱える値を超え
// 収束判定が出来ない為別途ルーチンを分けて計算します。
function TForm1.R_jacobi_sn(u, k: double): double;
const
  Keps = 1E-12;
  KN   = 1000;
var
  ALFRG     : byte;
  deg       : double;
  ud, ddeg  : double;
  i         : integer;
  absu      : double;
begin
  absu := abs(u);                                      // uの絶対値
  ddeg := 45;                                          // 初期補正角度設定
  if abs(k) = 1 then begin                             // 離心率kが1の時  90°だと∞になるので別途計算
    deg:= 90 - sqrt(Keps);                             // 90°に近い値セット
    ud := calc_first_elliptic_integral(k, deg, ALFRG); // 第一種楕円積分
    if ud < absu then begin                            // 90°に近い値より大きかったら
      if u < 0 then deg := -deg;
      result := deg;                                   // 90°に近い値を戻り値に
      Label3.Caption := 'Loop数= 0';
      exit;
    end;
    deg := 0;                                          // 初期角度セット
    for i := 0 to KN do begin
      ud := calc_first_elliptic_integral(k, deg, ALFRG);    // 第一種楕円積分
      if abs(ud - absu) < Keps then break;             // 積分値と指定値の差が誤差設定値より小さくなったら終了
      if ud < absu then begin                          // 積分値が指定値より小さかったら
        if deg + ddeg >= 90 then begin                 // 補正値を加算した場合90°を超える場合
          ddeg := 90 - deg - sqrt(Keps);               // 90°を超えない範囲に補正値を修正
        end;
      end
      else begin                                       // 指定値より大きかったら
        deg := deg - ddeg;                             // 角度減算
        ddeg := ddeg / 2;                              // 補正値2分の1
      end;
      deg := deg + ddeg;                               // 角度補正値加算
      if ddeg < Keps then break;                       // 角度補正値が収束判定値より小さくなったら終了
    end;
  end
  else begin                                           // 離心率が1以下の場合
    deg := 45;                                         // 初期値設定
    for i := 0 to KN do begin
      ud := calc_first_elliptic_integral(k, deg, ALFRG);  // 第一種楕円積分
      if abs(ud - absu) < Keps then break;             // 積分値と指定値の差が誤差設定値より小さくなったら終了
      if ud > absu then begin                          // 積分値が指定値より大きかったら
        deg := deg - ddeg;                             // 補正値減算
        ddeg := ddeg / 2;                              // 補正値2分の1に
      end;
      deg := deg + ddeg;                               // 補正値加算
      if ddeg < Keps then break;                       // 角度補正値が収束判定値より小さくなったら終了
    end;
  end;
  Label3.Caption := 'Loop数= ' + intTostr(i);
  if u < 0 then deg := -deg;
  result := deg;
end;

// 第一種楕円積分逆変換
// jacobi_snの場合は値は sn u = -1<= u' <=1
// uの値は -pi/2 <= u <= pi/2
// 離心率は -1<= k <=1 となります。
// 第一種楕円積分の逆関数という事で制限を外しました。
procedure TForm1.jacobi_sn_btnClick(Sender: TObject);
var
  u, k, sn : double;
  ch:      integer;
  snsin    : double;
begin
  val(Edit5.Text, u, ch);
  if ch <> 0 then begin
    application.MessageBox('first elliptic integral (u)に間違いがあります。','注意',0);
    exit;
  end;
//  if abs(u) > pi/ 2 then begin
//    application.MessageBox('uの最大値は±pi/2です。','注意',0);
//    exit;
// end;
  val(LabeledEdit3.Text, k, ch);
  if ch <> 0 then begin
    application.MessageBox('k(離心率)に間違いがあります。','注意',0);
    exit;
  end;
//  if abs(k) > 1 then begin
//    application.MessageBox('k(離心率)の最大値は±1です。','注意',0);
//    exit;
// end;
  sn := R_jacobi_sn(u, k);
  Labelededit4.Text := floatTostrF(sn, ffFixed, 10, 5);
  snsin := sin(sn / 180 * pi);
  Labelededit5.Text := floatTostrF(snsin, ffFixed, 10, 8);
end;

// 第二種楕円積分逆変換ルーチン
function TForm1.arcE(u, k: double): double;
const
  KN = 1000;
  Keps = 1E-12;
var
  deg       : double;
  ud, ddeg  : double;
  i         : integer;
  ALFRG     : byte;
begin
  ddeg := 45;                                     // 角度補正値
  deg := ddeg;                                    // 初期値
  for i := 0 to KN do begin
    ud := calc_second_elliptic_integral(k, deg, ALFRG);  // 第二種楕円積分
    if abs(ud - abs(u)) < Keps then break;        // 積分値と指定値の差が誤差設定値より小さくなったら終了
    if ud > abs(u) then begin                     // 指定値より大きかったら
      deg := deg - ddeg;                          // 補正値減算
      ddeg := ddeg / 2;                           // 補正値二分の一
    end;
    deg := deg + ddeg;                            // 補正値加算
    if ddeg < Keps then break;                    // 角度補正値が収束判定値より小さくなったら終了
  end;
  if u < 0 then deg := -deg;
  Label7.Caption := 'Loop数= ' + intTostr(i);
  result := deg;
end;

// 第二種楕円積分逆変換
procedure TForm1.arcE_btnClick(Sender: TObject);
var
  u, k, en : double;
  ch:      integer;
  ensin    : double;
begin
  val(Edit3.Text, u, ch);
  if ch <> 0 then begin
    application.MessageBox('second elliptic integralに間違いがあります。','注意',0);
    exit;
  end;
  val(LabeledEdit3.Text, k, ch);
  if ch <> 0 then begin
    application.MessageBox('k(離心率)に間違いがあります。','注意',0);
    exit;
  end;
  en := arcE(u, k);
  Labelededit6.Text := floatTostrF(en, ffFixed, 10, 5);
  ensin := sin(en / 180 * pi);
  Labelededit7.Text := floatTostrF(ensin, ffFixed, 10, 8);
end;

// 第三種楕円積分逆変換ルーチン
function TForm1.arc_pi(a, u, k: double): double;
const
  Keps = 1E-12;
  KN   = 1000;
var
  ALFRG     : byte;
  deg       : double;
  ud, ddeg  : double;
  eps       : double;
  i         : integer;
  absu      : double;
begin
  ALFRG := 0;
  eps := Keps;
  ddeg := 45;
  deg := ddeg;
  absu := abs(u);
  if abs(k) = 1 then begin                              // 離心率kが1の時  90°だと∞になるので別途計算
    deg:= 90 - sqrt(Keps);                              // 90°に近い値セット
    ud := calc_third_elliptic_integral(a, k, deg, ALFRG);     // 第三種楕円積分
    if ud < absu then begin                             // 90°に近い値より大きかったら
      if u < 0 then deg := -deg;
      result := deg;                                    // 90°に近い値を戻り値に
      Label8.Caption := 'Loop数= 0';
      exit;
    end;
    deg := 0;                                           // 初期角度セット
    for i := 0 to KN do begin
      ud := calc_third_elliptic_integral(a, k, deg, ALFRG);    // 第三種楕円積分
      if abs(ud - absu) < Keps then break;              // 積分値と指定値の差が誤差設定値より小さくなったら終了
      if ud < absu then begin                           // 積分値が指定値より小さかったら
        if deg + ddeg >= 90 then begin                  // 補正値を加算した場合90°を超える場合
          ddeg := 90 - deg - sqrt(Keps);                // 90°を超えない範囲に補正値を修正
        end;
      end
      else begin                                        // 指定値より大きかったら
        deg := deg - ddeg;                              // 角度減算
        ddeg := ddeg / 2;                               // 補正値2分の1
      end;
      deg := deg + ddeg;                                // 角度補正値加算
      if ddeg < Keps then break;                        // 角度補正値が収束判定値より小さくなったら終了
    end;
    Label8.Caption := 'Loop数= ' + intTostr(i);
    result := deg;
    exit;
  end;                                                  // 離心率kが1以下の計算
  for i := 0 to KN do begin
    ud := calc_Third_elliptic_integral(a, k, deg, ALFRG);      // 第三種楕円積分
    if abs(ud - absu) < eps then begin                  // 積分値と指定値の差が誤差設定値より小さくなったら終了
      if u < 0 then deg := -deg;
      break;
    end;
    if ud > absu then begin                             // 積分値が指定値より大きかったら
      deg := deg - ddeg;                                // 角度減算
      ddeg := ddeg / 2;                                 // 補正値2分の1
    end;
    deg := deg + ddeg;                                  // 角度補正値加算
    if ddeg < Keps then break;                          // 角度補正値が収束判定値より小さくなったら終了
  end;
  Label8.Caption := 'Loop数= ' + intTostr(i);
  result := deg;
end;

// 第三種楕円積分逆変換
procedure TForm1.arc_pi_btnClick(Sender: TObject);
var
  a        : double;
  u, k, pn : double;
  ch:      integer;
  pnsin    : double;
begin
  val(LabeledEdit1.Text, a, ch);
  if ch <> 0 then begin
    application.MessageBox('a(特性)の値に間違いがあります。','a(特性)',0);
    exit;
  end;
  val(Edit1.Text, u, ch);
  if ch <> 0 then begin
    application.MessageBox('Third elliptic integralに間違いがあります。','注意',0);
    exit;
  end;
  val(LabeledEdit3.Text, k, ch);
  if ch <> 0 then begin
    application.MessageBox('k(離心率)に間違いがあります。','注意',0);
    exit;
  end;
  pn := arc_pi(a, u, k);
  Labelededit8.Text := floatTostrF(pn, ffFixed, 10, 5);
  pnsin := sin(pn / 180 * pi);
  Labelededit9.Text := floatTostrF(pnsin, ffFixed, 10, 8);
end;

// Jacobi関数 Jacobi sn(u, k)
function TForm1.Jacobi_sn(u, k: double): double;
const
  N = 30;
  EPSILON = 1E-15;
var
  a: array[0..N] of Extended;
  g: array[0..N] of Extended;
  c: array[0..N] of Extended;
  two_n : Extended;
  phi   : Extended;
  half  : Extended;
  i, j  : integer;
begin
  half := 1 / 2;
  if k = 0 then begin
    result := sin(u);
    exit;
  end;
  if k = 1 then begin
    result := sin(2 * arctan(exp(u)) - pi / 2);
    exit;
  end;
  a[0] := 1;
  g[0] := sqrt(1 - k * k);
  c[0] := k;
  two_n := 1;
  for i := 0 to N do begin
    if abs(a[i] - g[i]) < a[i] * EPSILON then break;
    two_n := two_n + two_n;
    a[i + 1] := half * (a[i] + g[i]);
    g[i + 1] := sqrt(a[i] * g[i]);
    c[i + 1] := half * (a[i] - g[i]);
  end;
  phi := two_n * a[i] * u;
  for j := i downto 1 do
    phi := half * (phi + arcsin(c[j] * sin(phi) / a[j]));
  result := sin(phi);
end;

// 縄跳びの縄弛み計算
procedure TForm1.nawaBtnClick(Sender: TObject);
const
  kN  = 400;
var
  ALFRG     : byte;
  k, deg    : double;
  ch        : integer;
  a, a2     : double;
  fkd, b, u : double;
  c, L      : double;
  Ek        : double;
  i         : integer;
  x, y, dx  : double;
  snuk      : double;
  ty, by    : double;
  lx, rx    : double;
  def, ddf  : double;
  siguma    : double;
  Lp        : double;
  LLp       : double;
  dx2       : double;
  yb        : double;
begin
  val(Labelededit10.Text, a2, ch);
  if ch <> 0 then begin
    application.MessageBox('縄跳び紐支持間に間違いがあります。','注意',0);
    exit;
  end;
  val(LabeledEdit11.Text, k, ch);
  if ch <> 0 then begin
    application.MessageBox('縄跳び紐離心率Kに間違いがあります。','注意',0);
    exit;
  end;
  if abs(k) >= 1 then begin
    application.MessageBox('k(離心率)は最大値<abs(±1)です。','注意',0);
    exit;
  end;
  val(LabeledEdit12.Text, Lp, ch);
  if ch <> 0 then begin
    application.MessageBox('左からの位置に間違いがあります。','注意',0);
    exit;
  end;
  memo1.Clear;
  ALFRG := 0;
  deg := 180;                                                   //
  fkd := calc_first_elliptic_integral(k, deg, ALFRG);           // 180°第一種楕円積分
  if fkd = 0 then begin
    memo1.Lines.Add('支持間の距離係数 第一種楕円積分の');
    memo1.Lines.Add('積分値の値がゼロになります。');
    memo1.Lines.Add('Kの値を1以下にして下さい。');
    exit;
  end;
  // 各種計算
  a := a2 / 2;
  c := a2 / fkd;                                                // 積分値に対する補正係数
  b := 2 * k / (1 - k * k) * c;                                 // 最大値 90°の位置
  deg := 180;
  Ek := calc_second_elliptic_integral(k, deg, ALFRG);           // 第二種積分(180°)
  L := 2 * c * Ek / (1 - k * k) - a2;                           // 弧の長さ計算
  memo1.Lines.Add('補正係数        c= ' + floatTostrF(c, ffFixed, 10, 5));
  memo1.Lines.Add('紐の中心高さ      b= ' + floatTostrF(b, ffFixed, 10, 5));
  memo1.Lines.Add('紐の   長さ      L= ' + floatTostrF(L, ffFixed, 10, 5));
  Series1.Clear;
  Series2.Clear;
  // グラフスケール設定
  if a2 > b then begin
    ddf := a2 / 10;
    def := (a2 - b) / 2;
    ty := b + def + ddf;
    by := 0 - def - ddf;
    rx :=  a + ddf;
    lx := -a  - ddf;
  end
  else begin
    ddf := b / 10;
    def := (b - a2) / 2;
    ty := b + ddf;
    by := 0 - ddf;
    rx :=  a + def + ddf;
    lx := -a - def - ddf;
  end;
  // グラフのスケール設定値セット
  Series2.AddXY(lx, ty);
  Series2.AddXY(lx, by);
  Series2.AddXY(rx, by);
  application.ProcessMessages;
  // グラフ作図と分割積分
  dx := a2 / KN;                                    // ΔX
  // 0~KN 計算作図   距離積分
  Series1.AddXY(-a, 0);                             // グラフに追加
  for i := 1 to KN do begin
    x := i * dx;                                    // x位置
    u := x / c;                                     // F(α,k)=u
    // jacobi_sn  ヤコビの楕円関数
    snuk := jacobi_sn(u, k);
    y := snuk * b;                                  // x位置のyの値
    Series1.AddXY(x - a, y);                            // グラフに追加
  end;
  // グラフ作図と分割積分
  dx := Lp / KN;                                    // ΔX
  // 0~KN 計算作図   距離積分
  yb := 0;
  dx2 := dx * dx;                                   // dx^2
  siguma := 0;                                      // 合計値クリア
  for i := 1 to KN do begin                         // 指定位置長さ積分
    x := i * dx;                                    // x位置
    u := x / c;                                     // F(α,k)=u
    // jacobi_sn  ヤコビの楕円関数
    snuk := jacobi_sn(u, k);
    y := snuk * b;                                  // x位置のyの値
    ty := (y - yb);                                 // Δy
    siguma := siguma + sqrt(dx2 + ty * ty);         // 長さの合計
    yb := y;
  end;
  memo1.Lines.Add('指定位置分割計算1 L= ' + floatTostrF(siguma, ffFixed, 10, 5));
  application.ProcessMessages;
  // 長さ分割積分
  dx := Lp / (KN div 4);                            // ΔX
  dx2 := dx / 2;                                    // ΔX / 2
  siguma := 0;                                      // 合計値クリア
  for i := 0 to (KN div 4) - 1 do begin
    x := i * dx + dx2;                              // +dx2 は x位置中間位置で計算の為
    u := x / c;                                     // F(α,k)=u
    // jacobi_sn
    snuk := jacobi_sn(u, k);                        // jacobi_sn(u, k)が角度の為sin(α°)計算
    y := (1 - k * k * snuk * snuk);                 // dn^2(u) = 1-K^2*sn^2(u)
    siguma := siguma + y;                           // 積分 合計値
  end;
  LLP := 2 / (1- k * k) * siguma * dx - Lp;         // 距離
  memo1.Lines.Add('指定位置分割計算2 L= ' + floatTostrF(LLP, ffFixed, 10, 5));
end;

end.


  download inverse_elliptic_integra.zip


各種プログラム計算例に戻る