t分布

 母集団の平均と分散が判らず、標本サイズが小さい場合に平均を推定するのに使用されます。
詳細は、ウィキペディア t分布を参照して下さい。
重要な分布の一つとなっているので、t分布で、検索すれば説明が沢山みつかります。

 左の計算式は、下側の累積確率を求めるものです。
正則ベーター関数を利用して計算されます。



  f(x,v)は、確率密度、P(x,v)は下側累積確率、Q(x,v)は上側累積確率を表しています。
全体の累積確率は1なので下側累積確率が計算出来れば、上側累積確率を計算出来ます。
プログラムの確率密度は、ウィキペディアにある関数式で計算しています。

プログラム
  プログラムは、C言語辞典 アルゴリズムにあるt分布をDlphi用に変換したものに、確率密度の計算とグラフを追加しています。
累積確率の計算は、二つ用意して、同じ答えが出ることにより検算しています。

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, Vcl.ExtCtrls,
  VCLTee.Series, VCLTee.TeEngine, VCLTee.TeeProcs, VCLTee.Chart, system.UITypes, system.Math;

type
  TForm1 = class(TForm)
    Chart1: TChart;
    Memo1: TMemo;
    LabeledEdit1: TLabeledEdit;
    Button1: TButton;
    LabeledEdit2: TLabeledEdit;
    Series1: TLineSeries;
    Series2: TLineSeries;
    Series3: TPointSeries;
    CheckBox1: TCheckBox;
    Series4: TLineSeries;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

// ************ loggamma(x) ************
const

//ベルヌーイ数B(2)、B(4)、B(6)、...、B(16)

  B : array[1..8] of double = ( 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);        // B(16)

  m: integer = sizeof(B) div sizeof(double);

function loggamma(x: double): double;             // ログガンマ関数
var
  i : integer;
  sum : double;
  xx, v  : double;
  xj  : double;
  lng : double;
  ln_sqrt_2pi: double;
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 := x * x;
  xj := x;
  lng := ln_sqrt_2pi - x + (x - 0.5) * ln(x) - ln(v);
  for i := 1 to  m do begin
    sum := sum + B[i] / (i * 2 * (2 * i - 1)) / xj;
    xj := xj * xx;
  end;
  result := sum + lng;
end;

//**********************************************************

function p_beta(x, a, b : double): double;
var
  k : integer;
  p1, q1, p2, q2, d, previous : double;
begin
  if a <= 0 then begin
    result :=  MaxDouble;
    exit;
  end;
  if b <= 0 then begin
    if x <  1 then begin
      result := 0;
      exit;
    end;
    if x = 1 then begin
      result := 1;
      exit;
    end;
    result :=  MaxDouble;
    exit;
  end;
  if x > (a + 1) / (a + b + 2) then begin
    result := 1 - p_beta(1 - x, b, a);
    exit;
  end;
  if x <= 0 then begin
    result := 0;
    exit;
  end;
  p1 := 0;
  q1 := 1;
  p2 := exp(a * ln(x) + b * ln(1 - x)
    + loggamma(a + b) - loggamma(a) - loggamma(b)) / a;
  q2 := 1;
  k := 0;
  while k < 200 do begin
    previous := p2;
    d := - (a + k) * (a + b + k) * x
      / ((a + 2 * k) * (a + 2 * k + 1));
    p1 := p1 * d + p2;
    q1 := q1 * d + q2;
    inc(k);
    d := k * (b - k) * x / ((a + 2 * k - 1) * (a + 2 * k));
    p2 := p2 * d + p1;
    q2 := q2 * d + q1;
    if q2 = 0 then begin
      p2 := Maxdouble;
      continue;
    end;
    p1 := p1 / q2;
    q1 := q1 / q2;
    p2 := p2 / q2;
    q2 := 1;
    if p2 = previous then begin
      result := p2;
      exit;
    end;
  end;
  Form1.Memo1.Lines.Add('p_beta: 収束しません');
  result := p2;
end;

function q_beta(x, a, b: double): double;
begin
  result := 1 - p_beta(x, a, b);
end;

//**********************************************************

function p_t(t : double; df: integer): double;  // t 分布の下側確率
begin
  result := 0.5 * p_beta(df / (df + t * t), 0.5 * df, 0.5);
  if t >= 0 then
    result := 1 - result;
end;

{
function P_t(x: double; df: integer): double;
var
  y: double;
begin
  y := (x + sqrt(x * x + df)) / (2 * sqrt(x * x + df));
  result := p_beta(y, df/ 2, df / 2);
end;
}

function q_t(t : double; df: integer): double;  // t 分布の上側確率
begin
  result := 0.5 * p_beta(df / (df + t * t), 0.5 * df, 0.5);
  if t < 0 then result := 1 - result;
end;

//**********************************************************

function tf(t: double; df : integer): double;   // 確率密度
var
  p1, p2, p3 : double;
begin
  p1 := exp(loggamma((df + 1) / 2));
  p2 := exp(loggamma((df) / 2)) * sqrt(df * pi);
  p3 := power(1 + t * t / df, -(df + 1) / 2);
  result := p1 / p2 * p3;
end;

//**********************************************************

function p_t2(t: double; df: integer): double;  // t分布の下側累積確率
var
  i : integer;
  c2, p, s : double;
begin
  c2 := df / (df + t * t);  // cos^2
  s := sqrt(1 - c2);        // sin
  if t < 0 then s := -s;
  p := 0;
  i := df mod 2 + 2;
  while i <= df do begin
    p := p + s;
    s := s * (i - 1) * c2 / i;
    inc(i, 2);
  end;
  if (df and 1) <> 0 then		// 自由度が奇数
    result :=  0.5 + (p * sqrt(c2) + arctan(t / sqrt(df))) / PI
  else			                // 自由度が偶数
    result :=  (1 + p) / 2;
end;

function q_t2(t: double; df: integer): double;  // t分布の上側累積確率
begin
  result := 1 - p_t2(t, df);
end;

//**********************************************************/

procedure TForm1.Button1Click(Sender: TObject);
var
  ch   : integer;
  x, xi : double;
  Po, Qo : double;
  fo: double;
  df : integer;
  dx : double;
  min, max: double;
  I: Integer;
  GF : boolean;
begin
  val(LabeledEdit1.Text, x, Ch);
  if ch <> 0 then begin
    MessageDlg('x値に間違いがあります。', mtInformation, [mbOk], 0, mbOk);
    exit;
  end;
  val(LabeledEdit2.Text,  df, Ch);
  if ch <> 0 then begin
    MessageDlg('V値に間違いがあります。', mtInformation, [mbOk], 0, mbOk);
    exit;
  end;
  if df < 0 then begin
    MessageDlg('Vの値がゼロ以下です。', mtInformation, [mbOk], 0, mbOk);
    exit;
  end;

  Memo1.Clear;
  fo := tf(x, df);
  Memo1.Lines.Add('確率密度 f  ' + floatTostr(fo));
  if checkBox1.Checked = false then begin
    Po := p_t(x, df);
    Qo := q_t(x, df);
  end
  else begin
    Po := p_t2(x, df);
    Qo := q_t2(x, df);
  end;
  Memo1.Lines.Add('下側累積確率 P  ' + floatTostr(Po));
  Memo1.Lines.Add('上側累積確率 Q  ' + floatTostr(Qo));
  Series1.Clear;
  Series2.Clear;
  Series3.Clear;
  Series4.Clear;
  Series3.AddXY(x, fo);
  min := -5;
  max := 5;
  if x <= min + 0.5 then min := x - 1;
  if x >= max - 0.5 then max := x + 1;
  dx := (max - min) / 200;
  Gf := False;
  for I := 0 to 200 do begin
    xi := I * dx + min;
    fo := tf(xi, df);
    if xi <= x then begin
      Series1.AddXY(xi, fo);
      Series4.AddXY(xi, 0,'', clRed);
      Series4.AddXY(xi, fo,'', clRed);
      Series4.AddXY(xi, 0,'', clRed);
    end
    else begin
      if Gf = False then begin
        Series1.AddXY(xi - dx, 0);
        Series1.AddXY(xi, 0);
        Series1.AddXY(xi, fo);
        Gf := True;
      end;
      Series2.AddXY(xi, fo);
      Series4.AddXY(xi, 0,'', clblue);
      Series4.AddXY(xi, fo,'', clblue);
      Series4.AddXY(xi, 0,'', clblue);
    end;
  end;
end;

end.

 
  download Students_t_distribution.zip

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