逆誤差関数(Inverse error functtion)

名前の通り、誤差関数値から元の値を値を求めるものです。


c0=1

 上記の計算で、問題になるのは、 Ckの値で、この計算は、kの値が大きくなるにつれて、計算量が非常に多くなります。
誤差関数の値は-1~1なのですが、誤差関数 0.999の値を求めようとすると、kの最大値は、100000となってしまいます。
Ckの値が既知ならば問題ないのですが、配列を用意して100000迄計算すると、此処のプログラムでは半日以上掛かります。
一度計算をしておいて、ファイルに保存しておけば読みだして再利用が可能です。

 Ckの配列は、プログラムの起動時1000個を作成します。
しかし1000個では、y=0.937迄しか対応できません。
Ckの配列を半日かけて100000個分作成しても対応出来るのはy=0.999375迄です。
 現実には、それほど、有効桁数を必要としないと思われるので、c++のerfinv計算を移植してみました。
C++では、Long Double で10byte演算だったのですが、現在は64ビットモードだと8byte演算Doubleで計算されるので、演算精度が不足します。
そこで、多倍長演算をして、表示16桁にしてみました。
16桁正しく演算されます。
0~0.9999999999999999999999999999999999999999999999999999999999999999999999999999
の範囲でテストをしてみましたが、16桁正しい値を返します。
Doubleで計算すると、15桁の精度となりyの値が0.9999を超えた辺りから多少の誤差が出るようです。
此処のプログラムには組み込んでありませんが、x86モードの10byte精度(extended)で 0.9999999迄は正しい答えを返します。
 参考に演算精度が数桁で良いのならとsingleでのプログラムも変換してみました。
これは、完全に4byte演算です。


プログラム

Biginteger & Bigdecimal の組み込み方は第1種ケルビン関数を参照して下さい。

// Inverse Error function
// Ck値によるinvErfの計算の場合、Ckの配列をかなり大きくしても、0.999・・・の値の場合配列の数が不足します。
// プログラムでは1000k(100万)迄作成出来るようにしてありますが100k迄しかテストしていません。
// 100kの作成で、PCにもよりますが10時間程度必要とします。1000k作成すると日単位となって現実的でありません。
// 100kで0.99937程度までは50桁の答えが計算できます。
// プログラム起動時は1kに設定してあります。
// 精度と必要性から考えると、近似計算の16桁の精度計算で良いようにおもわれます。
// 近似計算の場合0.99・・・50桁以上でも問題なく計算できます。
// 近似計算はc言語のerfinv関数をdelphi bigdecimal用に変換したものです。
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, system.UITypes;

type
  TForm1 = class(TForm)
    CalcBtn: TButton;
    Memo1: TMemo;
    yEdit: TLabeledEdit;
    GroupBox1: TGroupBox;
    CkEdit: TLabeledEdit;
    Label1: TLabel;
    mbtn: TButton;
    cbtn: TButton;
    sbtn: TButton;
    rbtn: TButton;
    SaveDialog1: TSaveDialog;
    OpenDialog1: TOpenDialog;
    MaxloopsLabel: TLabel;
    procedure CalcBtnClick(Sender: TObject);
    procedure mbtnClick(Sender: TObject);
    procedure cbtnClick(Sender: TObject);
    procedure sbtnClick(Sender: TObject);
    procedure rbtnClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses system.Math, Velthuis.BigIntegers, Velthuis.Bigdecimals;

const
  DPS = 200;                          // 除算時有効桁数
  EPS = '1e-60';                      // 収束判定値デフォルト
  DPSCK = DPS div 2;
var
  KMAX  : integer;
  Kmaxm : integer;
  Cka : array of bigdecimal;
  Ckm : array of bigdecimal;
  BR : boolean;
  F1 : textFile;

//--------------------------------- Ln(x)---------------------------------------
// x = m * 2^n のnの値と2^nの値を戻します
// result = n
// ta     = 2^n
// m = x / 2^n
function frexpa(x: bigdecimal; var ta: biginteger): integer;
var
  tb: biginteger;
  xi, one: bigdecimal;
  n, j, s, pcs : integer;
begin
  pcs := BigDecimal.DefaultPrecision;
  one := '1';
  one := one.RoundToPrecision(pcs);
  xi := x;                          // x保存
  if xi < one then x := one / x;    // 1より小さかったら逆数
  ta := '1';
  n := 0;
  j := 0;                           // シフト回数クリア
  ta := '1';                        // ta初期値
  s := 4096;                        // s=4^i    i = 6
  repeat
    while x > ta do begin
      biginteger.ShiftLeft(ta, s, tb);  // s分左シフト
      ta := tb;
      inc(j);                           // シフト回数インクリメント
    end;
    if (s > 1) and (j > 0) then begin   // ta > x になったら
      dec(j);                           // シフト回数デクリメント
      biginteger.Shiftright(ta, s, tb); // s分右シフト
      ta := tb;
    end;
    n := n + s * j;                     // 2^nのn計算
    j := 0;                             // シフト回数クリア
    s := s shr 2;                       // sの値を1/4にする   右へ2ビットシフト
//    s := s div 4;                       // sの値を1/4にする
  until s = 0;            // s= ...16, 4, 1, 0
  if xi < one then begin                // より小さかったら
    n := -n + 1;                        // 2^nのn値負数
    biginteger.Shiftright(ta, 1, tb);   // 上式+1により右シフト
    ta := tb;
  end;
  result := n                             // ta = 2^n
end;

// 小さい値のlog計算
// xi 実数
// dpcs 有効桁数
// ans log値
// 戻り値 Loopオーバーフロー 無し true  有り false
// loop制限の無い場合 1e-4~1e4 が計算出来る範囲
// loop制限 5.8e-3 ~1.73e2  (kk=10000の場合)
function Log_11(x: bigdecimal; dpcs: integer; var ans: bigdecimal): boolean;
const
  KK = 10000;
var
  one, two, x1, x2, i, s, last: bigdecimal;
  k : integer;
begin
  result := true;
  one := '1';
  two := '2';
  x1 := (x - one) / (x + one);                      // x1 = (x-1)/(x+1)
  x2 := x1 * x1;                                    // x1^2
  x2 := x2.RoundToPrecision(dpcs);
  i := one;                                         // i=1
  s := x1;                                          // s初期値
  k := 0;                                           // 長ループ脱出用カウンタ0
  repeat
    x1 := x1 * x2;                                  // x1^2n+1
    x1 := x1.RoundToPrecision(dpcs);
    i := i + two;                                   // 2n+1
    i := i.RoundToPrecision(dpcs);
    last := s;                                      // 判定用sの値保存
    s := s + x1 / i;                                // Σs = s + x1^2n+1 / (2n+1)
    s := s.RoundToPrecision(dpcs);                  // 収束判定の為指定有効桁数以下丸め
    inc(k);                                         // ループカウンタインクリメント
  until (last = s) or (k > KK);                     // 収束判定
  ans := two * s;
  if k > kk then result := false;
end;

// loge(x)    ln(x)
// 級数展開
// 1e±500000 程度が限度
// sm <xi< last の範囲外の場合 (0.7~xi~1.4) * 2^nに変換 xiが1に近いほど変換早い
function log_bigf(xi: bigdecimal): bigdecimal;
label
  EXT;
const
  KK = 10000;
var
  LOG2, SQRT2, two, last, s, sm, one, one1, k2, a: bigdecimal;
  k : integer;
  kb, ta : biginteger;
  pcsBack, dpcs : integer;
  rmback : BigDecimal.RoundingMode;
begin
  result := '0';
  if xi <= result then begin
    application.MessageBox('無効な値です。','注意',0);
    exit;
  end;
  sm := '1e-2';
  last := '5e1';
  pcsBack := BigDecimal.DefaultPrecision;             // 除算演算精度バックアップ
  rmBack := BigDecimal.DefaultRoundingMode;           // 除算丸めモードバックアップ
  dpcs := pcsBack + 5;                                // 指定精度 + α
  BigDecimal.DefaultPrecision := dpcs;
  BigDecimal.DefaultRoundingMode := rmNearesteven;
  one := '1';
  if xi = one then begin
    goto EXT;
  end;
  if (xi > sm) and (xi < last) then begin             // 0.01 < xi < 50の時はLog11計算
    if Log_11(xi, dpcs, a) then begin                 // log11
      result := a;
      goto EXT;
    end;
  end;
  one1 := '1e500000';
  if xi < one then begin                              // xi < 1
    last := one / xi;                                 // 1 / xi
    if last > one1 then begin                         // (1 / xi) > 1e500000
      application.MessageBox('値が小さすぎます。','注意',0);
      goto EXT;
    end;
  end;
  if xi > one1 then begin                             // xi > 1e500000
    application.MessageBox('値が大きすぎます。','注意',0);
    goto EXT;
  end;
  xi := xi.RoundToPrecision(dpcs);
  two := '2';
  Log_11(two, dpcs, LOG2);                            // log(2)
  SQRT2 := bigdecimal.Sqrt(two, dpcs * 2);            // √2
  k := frexpa(xi / SQRT2, ta);                        // x / √2 = m * 2^k  ta = 2^k
  kb := k;
  k2 := ta;                                           // 2^k  integer to decimal
  k2 := k2.RoundToPrecision(dpcs);
  if k < 0 then begin                                 // kが負数だったら
    xi := xi * k2;                                    // x * 2^k   x=0.707~1.414  √2
    xi := xi.RoundToPrecision(dpcs);
  end
  else begin
    xi := xi / k2;                                    // x / 2^k   x=0.707~1.414  √2
  end;
  if xi <> one then begin                             // xi <> 1 なら
    Log_11(xi, dpcs, s)                               // log(xi)
  end
  else s := '0';                                      // xi=1なら s=0
  result := LOG2 * kb + s;
EXT:
  result := result.RoundToPrecision(pcsBack);
  BigDecimal.DefaultPrecision := pcsBack;           // 除算演算精度復帰
  BigDecimal.DefaultRoundingMode := rmBack;         // 除算丸めモード復帰
end;

// log(x)
function log_big(xi: bigdecimal): bigdecimal;
begin
  result := log_bigf(xi);
end;

//----------------------------- errinv 10byte相当-------------------------------
// Cライブラリより変換
// 元は LongDouble 10byte演算でしたがwin11では無いため多倍長演算を使用
// 演算結果の表示時に16桁に丸めます。
// 近似計算用データーは20桁ありますが、誤差により16桁程度の精度となります。
// doubleでの計算でも15桁程度の精度は出ますが1に近い0.9999999999999999の場合無限大になります。
function Inverse_error_long_double(x : bigdecimal; var INF : integer): bigdecimal;
const
  DPSLD = 30;
var
  LN2 : bigdecimal;
  A : array[0..7] of bigdecimal;
  B : array[0..7] of bigdecimal;
  C : array[0..7] of bigdecimal;
  D : array[0..7] of bigdecimal;
  E : array[0..7] of bigdecimal;
  F : array[0..7] of bigdecimal;

  abs_x, r, num, den : bigdecimal;
  d1, d2 : bigdecimal;
  i : integer;

begin
  INF := 0;
  if (x < -1) or (x > 1) then begin
    result := 0;
    exit;
  end;

  if (x = -1) then begin
    INF := - 1;
    exit;
  end;
  if (x = 1) then begin
    INF := 1;
    exit;
  end;

  bigdecimal.DefaultPrecision := DPSLD;       // 除算30桁設定

  LN2 := '6.931471805599453094172321214581e-1';
  A[0] := '1.1975323115670912564578e0';
  A[1] := '4.7072688112383978012285e1';
  A[2] := '6.9706266534389598238465e2';
  A[3] := '4.8548868893843886794648e3';
  A[4] := '1.6235862515167575384252e4';
  A[5] := '2.3782041382114385731252e4';
  A[6] := '1.1819493347062294404278e4';
  A[7] := '8.8709406962545514830200e2';

  B[0] := '1.0000000000000000000e0';
  B[1] := '4.2313330701600911252e1';
  B[2] := '6.8718700749205790830e2';
  B[3] := '5.3941960214247511077e3';
  B[4] := '2.1213794301586595867e4';
  B[5] := '3.9307895800092710610e4';
  B[6] := '2.8729085735721942674e4';
  B[7] := '5.2264952788528545610e3';

  C[0] := '1.42343711074968357734e0';
  C[1] := '4.63033784615654529590e0';
  C[2] := '5.76949722146069140550e0';
  C[3] := '3.64784832476320460504e0';
  C[4] := '1.27045825245236838258e0';
  C[5] := '2.41780725177450611770e-1';
  C[6] := '2.27238449892691845833e-2';
  C[7] := '7.74545014278341407640e-4';

  D[0] := '1.4142135623730950488016887e0';
  D[1] := '2.9036514445419946173133295e0';
  D[2] := '2.3707661626024532365971225e0';
  D[3] := '9.7547832001787427186894837e-1';
  D[4] := '2.0945065210512749128288442e-1';
  D[5] := '2.1494160384252876777097297e-2';
  D[6] := '7.7441459065157709165577218e-4';
  D[7] := '1.4859850019840355905497876e-9';

  E[0] := '6.65790464350110377720e0';
  E[1] := '5.46378491116411436990e0';
  E[2] := '1.78482653991729133580e0';
  E[3] := '2.96560571828504891230e-1';
  E[4] := '2.65321895265761230930e-2';
  E[5] := '1.24266094738807843860e-3';
  E[6] := '2.71155556874348757815e-5';
  E[7] := '2.01033439929228813265e-7';

  F[0] := '1.414213562373095048801689e0';
  F[1] := '8.482908416595164588112026e-1';
  F[2] := '1.936480946950659106176712e-1';
  F[3] := '2.103693768272068968719679e-2';
  F[4] := '1.112800997078859844711555e-3';
  F[5] := '2.611088405080593625138020e-5';
  F[6] := '2.010321207683943062279931e-7';
  F[7] := '2.891024605872965461538222e-15';

  abs_x := x.Abs(x);
  if abs_x < '0.85' then begin
    d1 := '0.180625';
    d2 := '0.25';
    r := d1 - d2 * x * x;

    num := A[7];
    den := B[7];
    for i := 6 downto 0 do begin
      num := num * r + A[i];
      den := den * r + B[i];
    end;

    num := num.RoundToPrecision(DPSLD);
    den := den.RoundToPrecision(DPSLD);
    result := x * num / den;
    bigdecimal.DefaultPrecision := DPS;         // 除算有効桁数元に戻し
    exit;
  end;

  r := bigdecimal.Sqrt(LN2 - log_big(bigdecimal.One - abs_x), DPSLD  + DPSLD);

  if r <= '5.0' then begin
    d1 := '1.6';
    r := r - d1;

    num := C[7];
    den := D[7];
    for i := 6 downto 0 do begin
      num := num * r + C[i];
      den := den * r + D[i];
    end;
  end else begin
    d2 := '5.0';
    r := r - d2;

    num := E[7];
    den := F[7];
    for i := 6 downto 0 do begin
      num := num * r + E[i];
      den := den * r + F[i];
    end;
  end;
  num := num.RoundToPrecision(DPSLD);
  den := den.RoundToPrecision(DPSLD);

  result := num / den;
  if x < '0' then
    result := - result;
  bigdecimal.DefaultPrecision := DPS;         // 除算有効桁数元に戻し
end;

//------------------------------------- π--------------------------------------
// π
// https://www.kurims.kyoto-u.ac.jp/~ooura/pi_fft-j.html
// T.Ooura AGM アルゴリズ
function pi_big: Bigdecimal;
var
  n, dpcs : integer;
  rmback : BigDecimal.RoundingMode;
  SQRT_SQRT_EPSILON, c, b, e : BigDecimal;
  npow : Bigdecimal;
  a, one, two, four, five, eight : Bigdecimal;
begin
  rmBack := BigDecimal.DefaultRoundingMode;           // 除算丸めモードバックアップ
  dpcs := DPS + 5;                             // 指定精度 + α
  BigDecimal.DefaultPrecision := dpcs;
  BigDecimal.DefaultRoundingMode := rmNearesteven;

  one := '1';
  one := one.RoundToPrecision(dpcs);                  // oneの有効桁数をdpcsに設定
  two := '2';
  four := '4';
  five := '5';
  eight := '8';
  SQRT_SQRT_EPSILON := one / BigDecimal.IntPower(two, dpcs shl 0); // 収束判定値
  c := BigDecimal.Sqrt(one / eight, dpcs * 2);
  a := one + c + c + c;
  b := BigDecimal.Sqrt(a, dpcs * 2);
  e := b - five / eight;
  b := b + b;
  c := e - c;
  a := a + e;
  npow := '4';
  n := 0;
  while (e > SQRT_SQRT_EPSILON) and (n < 100) do begin
    npow := npow + npow;                              // 8,16,32,64
    e := (a + b) / two;                               // 平均値 除算有効桁数での丸め
    b := a * b;
    b := b.RoundToPrecision(dpcs);                    // pcs + α 丸め
    b := BigDecimal.Sqrt(b, dpcs * 2);                // 平方根有効桁数での丸め
    e := e - b;
    b := b + b;
    c := c - e;
    a := e + b;
    inc(n);
  end;
  e := e * e;
  e := e.RoundToPrecision(dpcs);                      // pcs + α 丸め
  e := e / four;                                      // e = e * e / 4
  a := a + b;
  result := (a * a - e - e / two) / (a * c - e) / npow;   // 除算の順番に注意
  result := result.RoundToPrecision(DPS);             // 指定の精度に丸め

  BigDecimal.DefaultPrecision := DPS;                 // 除算演算精度復帰
  BigDecimal.DefaultRoundingMode := rmBack;           // 除算丸めモード復帰
end;

//------------------------------------------------------------------------------
// 数値の後ろのゼロ消去
function ZeroErase(s : string): string;
const
  EP = 'e';
  ZC = '0';
  DT = '.';
var
  c : char;
  i, j, k, l : integer;
begin
  l := length(s);
  j := 1;
  for i := 1 to l do begin
    c := s[i];
    if c = EP then begin
      j := i - 1;
      break;
    end;
    j := i;
  end;
  result := '';
  if j < l then begin
    for i := l downto j + 1 do
    result := s[i] + result;
  end;
  K := 1;
  for i := j downto 1 do begin
    c := s[i];
    if c <> ZC then begin
      k := i;
      if c = DT then  k := k + 1;
      break;
    end;
  end;
  for i := k downto 1 do
    result := s[i] + result;
end;

//-----------------------------Ck[n]配列初期値計算------------------------------
// プログラム起動時Ck[n]の計算
procedure ck(k : integer);
var
  j : integer;
  top, Bottom : bigdecimal;
  one, mb, s : bigdecimal;

  procedure ckcalc(k : integer);
  var
    m : integer;
  begin
    one := '1';
    Cka[0] := one;
    mb := bigdecimal.Zero;
    s := 0;
    for m := 0 to k - 1 do begin
      Bottom := (mb + one) * (mb + mb + one);    // 分母
      top := CKa[m] * Cka[k - m - 1];            // 分子
      bottom := bottom.RoundToPrecision(DPS);
      top := top.RoundToPrecision(DPS);
      s := s + top / bottom;                     // Σ
      mb := mb + one;
    end;
    s := s.RoundToPrecision(DPSCK);
    if k > 0 then cka[k] := s;
  end;

begin
  for j := 0 to k do ckcalc(j);                  // Ck(0)~Ck(k)計算
end;

//---------------------------------erfinv---------------------------------------------
// InvErfのCk配列による計算
function Inverse_error(y: bigdecimal; var k : integer): bigdecimal;
var
  kb, s, pai, one, two, aks2k1, sd, abssd : bigdecimal;
  rps2y, rps2yh2 : bigdecimal;
  k2p1: bigdecimal;
begin
  one := bigdecimal.One;
  two := one + one;                           // 2
  pai := pi_big;                              // π
  pai := pai.Sqrt(pai, DPS + DPS);            // √(π)
  rps2y :=  pai / two * y;                    // √(π)/2 * y
  rps2yh2 := rps2y * rps2y;
  s := 0;
  k := 0;
  kb := bigdecimal.Zero;
  repeat
    k2p1 := kb + kb + one;
    aks2k1 := CKa[k] / k2p1;

    sd := rps2y * aks2k1;
    s := s + sd;
    kb := kb + one;
    rps2y := rps2y * rps2yh2;
    rps2y := rps2y.RoundToPrecision(DPS);
    inc(k);
    abssd := sd.Abs(sd);
  until (abssd < EPS) or (k > KMAX);

  result := s;
  form1.Memo1.Lines.Append('Loops = ' + inttostr(k - 1));
end;

//------------------------------  erfinv single ---------------------------------------
function erfinv_single(x : single): single;
var
  w, p: single;
begin
  if abs(x) = 1 then begin
    if x > 0 then result := infinity
             else result := -infinity;
    exit;
  end;
  w := - ln((1.0 - x) * (1.0+x));
  if  w < 5.0  then begin
    w :=  w - 2.5;
    p :=  2.81022636e-08;
    p :=  3.43273939e-07 + p * w;
    p := -3.5233877e-06 + p * w;
    p := -4.39150654e-06 + p * w;
    p :=  0.00021858087 + p * w;
    p := -0.00125372503 + p * w;
    p := -0.00417768164 + p * w;
    p :=  0.24664072 + p * w;
    p :=  1.50140941 + p * w;
  end
  else begin
    w := sqrt(w) - 3.0;
    p := -0.000200214257;
    p :=  0.000100950558 + p * w;
    p :=  0.00134934322 + p * w;
    p := -0.00367342844 + p * w;
    p :=  0.00573950773 + p * w;
    p := -0.0076224613 + p * w;
    p :=  0.00943887047 + p * w;
    p :=  1.00167406 + p * w;
    p :=  2.83297682 + p * w;
  end;
  result :=  p * x;
end;

//---------------------------------- calc --------------------------------------
// 入力値によるInvErf計算
procedure TForm1.CalcBtnClick(Sender: TObject);
var
  y, ans, absy : bigdecimal;
  yd, ansd : bigdecimal;
  n, INF : integer;
  sy, sans : single;
begin
  if not mbtn.Enabled then exit;
  try
    y := yedit.Text;
  except
    on EConverterror do begin
      MessageDlg('y の値に間違いがあります。', mtError, [mbOK], 0);
      exit;
    end;
  end;
  absy := y.Abs(y);
  if absy > bigdecimal.One then begin
    MessageDlg('y の絶対値|y|は1又は1以下にしてください。', mtWarning, [mbOK], 0);
    exit;
  end;
  //------------------------------- 多倍長 -------------------------------------
  calcBtn.Enabled := false;
  memo1.Clear;

  memo1.Lines.Append('inverse error function  (逆誤差関数)');
  memo1.Lines.Append(' 多倍長計算 50桁表示');
  if absy < bigdecimal.One then begin
    ans := Inverse_error(y, n);                 // Ck配列によるInvErfの計算
    if n > KMAX then
      memo1.Lines.Append('Ck配列の最大値を超えました答えは正しくありません。');
    ans := ans.RoundToPrecision(50);
    if ans = '0' then memo1.Lines.Append('inverf = 0.0')
                 else memo1.Lines.Append('inverf = ' + ZeroErase(ans.ToString));
  end
  else begin
    Memo1.Lines.Append('Loops = 0');
    if y > 0 then memo1.Lines.Append('inverf = ∞')
             else memo1.Lines.Append('inverf = -∞');
  end;
  //-------------------- 近似計算 多倍長 30桁 ----------------------------------
  memo1.Lines.Append('');
  memo1.Lines.Append(' 近似計算 30桁演算 16桁表示');
  yd := yedit.Text;
  ansd := Inverse_error_long_double(yd, INF);      // 近似値によるInvErfの計算
  if INF = 0 then begin
    ansd := ansd.RoundToPrecision(16);
    if ans = '0' then memo1.Lines.Append('inverf = 0.0')
                 else memo1.Lines.Append('inverf = ' + ansd.ToString);
  end
  else begin
    if INF = 1 then Memo1.Lines.Append('INF');
    if INF = -1 then Memo1.Lines.Append('-INF');
  end;
  //-------------------- 近似計算  single 演算 ---------------------------------
  memo1.Lines.Append('');
  memo1.Lines.Append(' 近似計算 Single演算');
  sy := strtofloat(yedit.Text);
  sans := erfinv_single(sy);
  memo1.Text := memo1.Text + 'inverf = ' + floatTostrF(sans, ffFixed, 7, 7);

  calcBtn.Enabled := true;
end;

//---------------------------- Ck[n]配列のファイル操作--------------------------
procedure CkmaxLoopsdisp(n: integer);
begin
  Form1.MaxloopsLabel.Caption := 'Ck配列数 ' + intTostr(n);
end;

// Ck[n]の新規計算--------------------------------------------------------------
// Ck[n]の初期値設定と殆ど同じですが、デバッグの都合上別に設定
procedure ckb(k : integer);
var
  j : integer;
  top, Bottom : bigdecimal;
  one, mb, s : bigdecimal;

  procedure ckcalc(k : integer);
  var
    m : integer;
  begin
    one := '1';
    Ckm[0] := one;
    mb := bigdecimal.Zero;
    s := 0;
    for m := 0 to k - 1 do begin
      Bottom := (mb + one) * (mb + mb + one);    // 分母
      top := CKm[m] * Ckm[k - m - 1];            // 分子
      bottom := bottom.RoundToPrecision(DPS);
      top := top.RoundToPrecision(DPS);
      s := s + top / bottom;                     // Σ
      mb := mb + one;
    end;
    s := s.RoundToPrecision(DPSCK);              // 有効桁数DPSの1/2
    if k > 0 then ckm[k] := s;
  end;

begin
  for j := 0 to k do begin              // j=0~k
    ckcalc(j);                          // ck(j)計算
    if j mod 100 = 0 then begin         // 100ループ一回 ループNo表示
      form1.Memo1.Lines.Append(' Ck作成 loops = ' + inttostr(j));
    end;
    application.ProcessMessages;
    if BR then break;                   // 中止入力でブレーク
  end;
  KMAXm := j - 1;                       // Ckの計算済み最大値
end;

// 新規配列作成の中止
procedure TForm1.cbtnClick(Sender: TObject);
begin
  BR := True;
end;

// Ck(n)の新規作成
procedure TForm1.mbtnClick(Sender: TObject);
var
  ch, k : integer;
begin
  if not calcbtn.Enabled then exit;

  val(ckEdit.Text, K, ch);
  if ch <> 0 then begin
    application.MessageBox('Ck(n)の値に間違いが在ります。','注意',0);
    exit;
  end;
  if K > 1000 then begin
    application.MessageBox('Ck(n)の値が大きすぎます1000K以下にしてください。','注意',0);
    exit;
  end;
  mbtn.Enabled := false;
  sbtn.Enabled := false;
  rbtn.Enabled := false;
  BR := False;
  KMAXm := k * 1000;
  setlength(Ckm, kMAXm + 1);          // Ck(n)の長さ設定
  ckb(KMAXm);                         // Ck(n)の新規計算
  KMAX := kMAXm;                      // 配列の最大値Ck(m)の新規計算の最大値に変更
  setlength(Cka, KMAX + 1);           // invEnf計算用Cka配列の長さを設定
  for k := 0 to  KMAX do cka[k] := ckm[k];    // 配列のコピー
  memo1.Lines.Append('Ck(n)の作成完了しました。' + 'Ck(n) nMax= ' + intTostr(KMAXm));
  CkmaxLoopsdisp(KMAX);
  mbtn.Enabled := true;
  sbtn.Enabled := true;
  rbtn.Enabled := true;
end;

// Ck[n]配列のファイルからの読み込み -------------------------------------------------
function dataread: integer;
var
  i, Km : integer;
  L : string;
begin
  result := 0;
  readln(f1, L);
  km := strtoint(L);
  setlength(ckm, km);
  for i := 0 to km do begin
    readln(f1, L);
    ckm[i] := L;
  end;
  result := km;
end;

procedure TForm1.rbtnClick(Sender: TObject);
var
  i, km : integer;
begin
  if OpenDialog1.Execute then begin
    if not FileExists(OpenDialog1.FileName) then exit;

    AssignFile(F1, OpenDialog1.FileName);
    reset(F1);
  end;
  try
    km := dataread;
  finally
    closeFile(F1);
  end;
  if km > 1000 then begin
    setlength(Cka, km);
    for i := 0 to km do Cka[i] := Ckm[i];
    KMAX := km;
    CkmaxLoopsdisp(KMAX);
  end;
end;

// Ck[n]配列のファイルへの保存 -------------------------------------------------
procedure datawrite;
var
  i : integer;
begin
  Writeln(F1,inttostr(KMAX));
  for i := 0 to KMAX do begin
    Writeln(F1, Cka[i].ToString);
  end;
end;

procedure TForm1.sbtnClick(Sender: TObject);
var
  LLS : string;
begin
  if SaveDialog1.Execute then begin
    LLS := ChangeFileExt(SaveDialog1.FileName,'.Ckd');
    if FileExists(LLS) then
      if MessageDlg('既にファイルが存在します 上書きしますか',mtConfirmation,
                           [mbYes, mbNo], 0) = mrNo then exit;
    AssignFile(F1,LLS); { ダイアログで選択されたファイル }
    ReWrite(F1);
    try
      datawrite;  // 修正データー書き込み
    finally
      closeFile(F1);
    end;
  end;
end;

//------------------------------------------------------------------------------
// 初期値設定
initialization
  kMAX := 1000;
  bigdecimal.DefaultPrecision := DPS;
  setlength(Cka, KMAX + 1);
  ck(KMAX);
end.

 
download inverse_error_function_test.zip



  三角関数、逆三角関数、その他関数 に戻る