連分数補間

 の独立変数で表されるカーブの値を連分数でフィッテングします。
xの値は連続して同じ値が有ってはなりません、又、直線となる三点の値があると、補間係数計算式の分母がゼロになり計算できません。
その場合、僅かに値をずらすことにより計算は可能となります。

 左図は三角関数の中間値を連分数で補間計算しその時の誤差をグラフにしたものですが、小さな誤差となっています。
三角関数の場合、その範囲が180°程度で有れば良くフィッテングします。
tan(x)に関しては、90度で無限大となる為、90°近辺に対しては無理が有ります。


 三角関数や対数のような関数に近い値のカーブの場合は、よくフィッテングしますが、そこから外れた場合に対しての余裕度は小さいようです。
逆に、連分数補間でフィッテングする場合は、何らかの関数に従った値と言えるでしょう。

プログラム 上図上

unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VclTee.TeeGDIPlus, Vcl.StdCtrls,
  VCLTee.TeEngine, VCLTee.Series, Vcl.ExtCtrls, VCLTee.TeeProcs, VCLTee.Chart;

type
  TForm1 = class(TForm)
    Chart1: TChart;
    Series1: TLineSeries;
    Button1: TButton;
    Series2: TLineSeries;
    Series3: TPointSeries;
    RadioGroup1: TRadioGroup;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
uses system.Math;

type
  DArray = array of double;

// 係数表計算
procedure maketable(x, y : DArray; N: integer);    // 係数を求めy[]に上書き
var
  i, j : integer;
begin
  for j := 0 to N - 1 do
    for i := j + 1 to N - 1 do begin
      if y[i] - y[j] = 0 then begin
        application.MessageBox(pchar('y' + inttostr(i+1) + '-y' + inttostr(j+1)
                                         + 'がゼロになります。'),'注意',0);
        exit;
      end;
      y[i] := (x[i] - x[j]) / (y[i] - y[j]);
    end;
end;

// 連分数による補間計算
function interpolate(x, y: DArray; N: integer; t: double): double;  // 補間
var
  i : integer;
  r : double;
begin
  r := y[N - 1];
  for i := N - 2 downto 0 do
    r := (t - x[i]) / r + y[i];
  result := r;
end;

// 補間計算グラフ作図
procedure TForm1.Button1Click(Sender: TObject);
const
  PITCH = 20;
var
  N : integer;
  i, imax : integer;
  s, t, e, es : double;
  x, y : Darray;                     // array of double
begin
  N := 5;                            // 0~80°
  t := 0;
  es := 1;
  case RadioGroup1.ItemIndex of
    0: N := 19;                      // 0~360°
    1: N := 10;                      // 0~180°
//    2: N := 5;                       // 0~80°
  end;
  setlength(x, N);
  setlength(y, N);
  Series1.Clear;
  Series2.Clear;
  Series3.Clear;
  for i := 0 to N - 1 do begin
    x[i] := PITCH * i;
    case RadioGroup1.ItemIndex of
      0: y[i] := sin(x[i] * pi / 180);
      1: y[i] := cos(x[i] * pi / 180);
      2: y[i] := tan(x[i] * pi / 180);
    end;
    Series3.AddXY(x[i], y[i]);
  end;
  maketable(x, y, N);                   // 表の作成
  case RadioGroup1.ItemIndex of
    0:  begin
          es := 1e6;
          chart1.RightAxis.Title.Text := '誤差×1E6';
        end;
    1:  begin
          es := 1e6;
          chart1.RightAxis.Title.Text := '誤差×1E6';
        end;
    2:  begin
          es := 1e3;
          chart1.RightAxis.Title.Text := '誤差×1E3';
        end;
  end;
  imax := (N - 1) * PITCH;
  for i := 0 to imax do begin           // グラフ作図
    case RadioGroup1.ItemIndex of
      0: t := sin(i * pi / 180);
      1: t := cos(i * pi / 180);
      2: t := tan(i * pi / 180);
    end;
    s := interpolate(x, y, N, i);       // 補間計算
    Series1.AddXY(i, s);
    e := (s - t) * es;                  // 誤差
    Series2.AddXY(i, e);
  end;
end;

end.

 プログラム 上図下 stringgridで、自由なx,yの値を入力出来るようにしたものです。
サンプルとして、tan(x)の値が表示されます。

unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VclTee.TeeGDIPlus, Vcl.StdCtrls,
  VCLTee.TeEngine, VCLTee.Series, Vcl.ExtCtrls, VCLTee.TeeProcs, VCLTee.Chart,
  Vcl.Grids;

type
  TForm1 = class(TForm)
    Chart1: TChart;
    Series1: TLineSeries;
    tanxBtn: TButton;
    Series2: TLineSeries;
    Series3: TPointSeries;
    StringGrid1: TStringGrid;
    NEdit: TLabeledEdit;
    NsetBtn: TButton;
    interpolationBtn: TButton;
    procedure tanxBtnClick(Sender: TObject);
    procedure NsetBtnClick(Sender: TObject);
    procedure interpolationBtnClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    procedure GridSet(Sin, Colin: integer);
    procedure FGridSet(Sin, Colin: integer);    // グリッドサイズ変更
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
uses system.Math;

type
  DArray = array of double;

// グリッドサイズの変更
procedure TForm1.GridSet(Sin, Colin: integer);    // グリッドサイズ変更
var                                               // データー行の数は10迄
  H, W:integer;                                // それ以上はスクロールする
begin
  if (StringGrid1.ColCount = Colin) and (StringGrid1.RowCount = Sin) then exit;
  StringGrid1.ColWidths[0] := 30;
  StringGrid1.ColCount := Colin;
  StringGrid1.RowCount := Sin;
  if Sin <= 11 then                               // ストリンググリッドの大きさ設定
    begin                                         // 固定行を含め11行又はそれ以下の場合
      H := (StringGrid1.DefaultRowHeight + 1) * Sin;
      W := (StringGrid1.DefaultColWidth + 1) * (Colin - 1) + 31;
      StringGrid1.ScrollBars := ssNone;           // スクロールバーなし
    end
    else
    begin                                         // 固定行を含め11行より多い場合
      H := (StringGrid1.DefaultRowHeight + 1) * (10 + 1);
      W := (StringGrid1.DefaultColWidth + 1) * (Colin - 1) + 31;
//      W := (StringGrid1.DefaultColWidth + 1) * Colin;
      StringGrid1.ScrollBars := ssVertical;       // スクロールバー表示
    end;
  StringGrid1.ClientWidth := W;
  StringGrid1.ClientHeight := H;
end;

// グリッド固定行の設定
procedure TForm1.FGridSet(Sin, Colin: integer);
var
  E : integer;
begin
  GridSet(Sin, Colin);
  for E := 1 to Sin - 1 do
      StringGrid1.Rows[E].Text := intTostr(E);
  StringGrid1.Cols[1].Text := 'x';
  StringGrid1.Cols[2].Text := 'y';
end;

// 係数テーブル設定
procedure maketable(x, y : DArray; N: integer);    // 係数を求めy[]に上書き
var
  i, j : integer;
begin
  for j := 0 to N - 2 do begin
    if x[j] = x[j + 1] then begin
      application.MessageBox(pchar('x' + inttostr(j+1) + ' x' + inttostr(j+2)
                                         + 'が同じ値です。'),'注意',0);
      exit;
    end;
  end;
  for j := 0 to N - 1 do
    for i := j + 1 to N - 1 do begin
      if y[i] - y[j] = 0 then begin
        application.MessageBox(pchar('y' + inttostr(i+1) + '-y' + inttostr(j+1)
                                         + 'がゼロになります。'),'注意',0);
        exit;
      end;
      y[i] := (x[i] - x[j]) / (y[i] - y[j]);
    end;
end;

// 補間計算
function interpolate(x, y: DArray; N: integer; t: double): double;  // 補間
var
  i : integer;
  r : double;
begin
  r := y[N - 1];
  for i := N - 2 downto 0 do
    r := (t - x[i]) / r + y[i];
  result := r;
end;

// 参考値tax(x)セット
procedure TForm1.tanxBtnClick(Sender: TObject);
var
  N : integer;
  i, imax : integer;
  s, t, e, es : double;
  x, y : Darray;
begin
  N := 5;
  Nedit.Text := inttostr(N);
  GridSet(N + 1, 3);
  setlength(x, N);
  setlength(y, N);
  Series1.Clear;
  Series2.Clear;
  Series3.Clear;
  for i := 0 to N - 1 do begin
    x[i] := 20 * i;
    y[i] := tan(x[i] * pi / 180);
    Series3.AddXY(x[i], y[i]);
    StringGrid1.cells[1,i + 1] := floatTostr(x[i]);
    StringGrid1.cells[2,i + 1] := floatTostr(y[i]);
  end;
  maketable(x, y, N);                   // 表の作成
  imax := (N - 1) * 20;
  es := 1e3;
  chart1.RightAxis.Title.Text := '誤差×1E3';

  for i := 0 to imax do begin
    t := tan(i * pi / 180);
    s := interpolate(x, y, N, i);       // 補間計算
    Series1.AddXY(i, s);
    e := (s - t) * es;
    Series2.AddXY(i, e);
  end;
end;

// グリッドN数セット
procedure TForm1.NsetBtnClick(Sender: TObject);
var
  N, ch : integer;
begin
  val(Nedit.Text, N, ch);
  if ch <> 0 then begin
    application.MessageBox('N数の入力に間違いが有ります。','注意',0);
    exit;
  end;
  if N > 100 then begin
    application.MessageBox('N数は100迄にして下さい。','注意',0);
    exit;
  end;
  GridSet(N + 1, 3);
end;

// グリッドの値による補間計算
procedure TForm1.interpolationBtnClick(Sender: TObject);
var
  N, ch, i, imax : integer;
  x, y : Darray;
  cn : string;
  s, dx, xx : double;
begin
  val(Nedit.Text, N, ch);
  if ch <> 0 then begin
    application.MessageBox('N数の入力に間違いが有ります。','注意',0);
    exit;
  end;
  if N > 100 then begin
    application.MessageBox('N数は100迄にして下さい。','注意',0);
    exit;
  end;
  GridSet(N + 1, 3);
  setlength(x, N);
  setlength(y, N);
  for i := 0 to N - 1 do begin
    val(StringGrid1.cells[1,i + 1], x[i], ch);
    if ch <> 0 then begin
      cn := 'x' + intTostr(i + 1) + 'に間違いが有ります。';
      application.MessageBox(pchar(cn),'注意',0);
      break;
    end;
    val(StringGrid1.cells[2,i + 1], y[i], ch);
    if ch <> 0 then begin
      cn := 'y' + intTostr(i + 1) + 'に間違いが有ります。';
      application.MessageBox(pchar(cn),'注意',0);
      break;
    end;
  end;
  if ch <> 0 then exit;
  Series1.Clear;
  Series2.Clear;
  Series3.Clear;
  for i := 0 to N - 1 do
    Series3.AddXY(x[i], y[i]);
  maketable(x, y, N);                     // 表の作成
  imax := (N - 1) * 20;
  chart1.RightAxis.Title.Text := '';
  dx := (x[N-1] - x[0]) / imax;
  for i := 0 to imax do begin
    xx := i * dx + x[0];
    s := interpolate(x, y, N, xx);        // 補間計算
    Series1.AddXY(xx, s);
  end;
end;

// グリッド初期設定
procedure TForm1.FormCreate(Sender: TObject);
begin
  FGridSet(101, 3);                 // 固定行1列1 + 100行2列まで行列を変更しても
                                    // 入力値がクリアされないようにします。
  GridSet(4, 3);                    // 最初の設定
end;

end.


download continued_fraction_F_interpolation.zip

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