Delphi 中卡方分布函数的代码
Code for Chi-square distribution function in Delphi
我一直在为 Delphi 中的 chi-square
发行版寻找可用的完整代码。网上有一些代码,但通常它们不起作用或缺少部分,无法编译等。还有一些库,但我对一些我可以简单实现的代码感兴趣。
我发现了一些几乎可以工作的东西。一些德语部分已修复,它编译并为大部分数据提供 p-values
:
function LnGamma (x : Real) : Real;
const
a0 = 0.083333333096;
a1 = -0.002777655457;
a2 = 0.000777830670;
c = 0.918938533205;
var
r : Real;
begin
r := (a0 + (a1 + a2 / sqr(x)) / sqr(x)) / x;
LnGamma := (x - 0.5) * ln(x) - x + c + r;
end;
function LnFak (x : Real) : Real;
var
z : Real;
begin
z := x+1;
LnFak := LnGamma(z);
end;
function Reihe (chi : Real; f : Real) : Real;
const MaxError = 0.0001;
var
Bruch,
Summe,
Summand : Real;
k, i : longint;
begin
Summe := 1;
k := 1;
repeat
Bruch := 1;
for i := 1 to k do
Bruch := Bruch * (f + 2 * i);
Summand := power(chi, 2 * k) / Bruch;
Summe := Summe + Summand;
k := succ(k);
until (Summand < MaxError);
Reihe := Summe;
end;
function IntegralChi (chisqr : Real; f : longint) : Real;
var
s : Real;
begin
S := power((0.5 * chisqr), f/2) * Reihe(sqrt(chisqr), f)
* exp((-chisqr/2) - LnGamma((f + 2) / 2));
IntegralChi := 1 - s;
end;
对于比较大的结果来说效果很好。
例如:
对于 Chi = 1.142132
和 df = 1
我得到 p
关于 0.285202
,这是完美的。与 SPSS
结果或其他程序相同。
但是例如 Chi = 138.609137
和 df = 4
我应该收到一些关于 0.000000
的信息,但是我在 Reiche
函数中遇到浮点溢出错误。 Summe
和Summand
那么大了。
我承认理解分布函数不是我的强项,所以也许有人会告诉我我做错了什么?
非常感谢您提供的信息
你应该调试你的程序,发现有溢出
在你的循环中 k=149。对于 k=148,Bruch 的值为 3.3976725289e+304。 Bruch 的下一次计算溢出。解决方法是代码
for i := 1 to k do
Bruch := Bruch / (f + 2 * i);
Summand := power(chi, 2 * k) * Bruch;
进行此更改后,您将在第 156 次迭代后获得值 IntegralChi(138.609137,4) = 1.76835197E-7
。
请注意,您的计算(即使对于这个简单的算法)不是最优的
因为您一遍又一遍地计算 Bruch 值。只更新一次
每个循环:
function Reihe (chi : Real; f : Real) : Real;
const MaxError = 0.0001;
var
Bruch,
Summe,
Summand : Real;
k : longint;
begin
Summe := 1;
k := 1;
Bruch := 1;
repeat
Bruch := Bruch / (f + 2 * k);
Summand := power(chi, 2 * k) * Bruch;
Summe := Summe + Summand;
k := succ(k);
until (Summand < MaxError);
Reihe := Summe;
end;
类似的考虑应该应用于计算power(chi, 2*k)
,然后将其与改进的 Bruch 评估相结合。
编辑: 作为对您评论的回应,这里是基于幂函数属性的改进版本,即power(chi, 2*(k+1)) = power(chi, 2*k)*sqr(chi)
function Reihe (chi : Real; f : Real) : Real;
const MaxError = 0.0001;
var
chi2,
Summe,
Summand : Real;
k : longint;
begin
Summe := 1;
k := 1;
Summand := 1;
chi2 := sqr(chi);
repeat
Summand := Summand * chi2 / (f + 2 * k);
Summe := Summe + Summand;
k := succ(k);
until (Summand < MaxError);
Reihe := Summe;
end;
我一直在为 Delphi 中的 chi-square
发行版寻找可用的完整代码。网上有一些代码,但通常它们不起作用或缺少部分,无法编译等。还有一些库,但我对一些我可以简单实现的代码感兴趣。
我发现了一些几乎可以工作的东西。一些德语部分已修复,它编译并为大部分数据提供 p-values
:
function LnGamma (x : Real) : Real;
const
a0 = 0.083333333096;
a1 = -0.002777655457;
a2 = 0.000777830670;
c = 0.918938533205;
var
r : Real;
begin
r := (a0 + (a1 + a2 / sqr(x)) / sqr(x)) / x;
LnGamma := (x - 0.5) * ln(x) - x + c + r;
end;
function LnFak (x : Real) : Real;
var
z : Real;
begin
z := x+1;
LnFak := LnGamma(z);
end;
function Reihe (chi : Real; f : Real) : Real;
const MaxError = 0.0001;
var
Bruch,
Summe,
Summand : Real;
k, i : longint;
begin
Summe := 1;
k := 1;
repeat
Bruch := 1;
for i := 1 to k do
Bruch := Bruch * (f + 2 * i);
Summand := power(chi, 2 * k) / Bruch;
Summe := Summe + Summand;
k := succ(k);
until (Summand < MaxError);
Reihe := Summe;
end;
function IntegralChi (chisqr : Real; f : longint) : Real;
var
s : Real;
begin
S := power((0.5 * chisqr), f/2) * Reihe(sqrt(chisqr), f)
* exp((-chisqr/2) - LnGamma((f + 2) / 2));
IntegralChi := 1 - s;
end;
对于比较大的结果来说效果很好。
例如:
对于 Chi = 1.142132
和 df = 1
我得到 p
关于 0.285202
,这是完美的。与 SPSS
结果或其他程序相同。
但是例如 Chi = 138.609137
和 df = 4
我应该收到一些关于 0.000000
的信息,但是我在 Reiche
函数中遇到浮点溢出错误。 Summe
和Summand
那么大了。
我承认理解分布函数不是我的强项,所以也许有人会告诉我我做错了什么?
非常感谢您提供的信息
你应该调试你的程序,发现有溢出 在你的循环中 k=149。对于 k=148,Bruch 的值为 3.3976725289e+304。 Bruch 的下一次计算溢出。解决方法是代码
for i := 1 to k do
Bruch := Bruch / (f + 2 * i);
Summand := power(chi, 2 * k) * Bruch;
进行此更改后,您将在第 156 次迭代后获得值 IntegralChi(138.609137,4) = 1.76835197E-7
。
请注意,您的计算(即使对于这个简单的算法)不是最优的 因为您一遍又一遍地计算 Bruch 值。只更新一次 每个循环:
function Reihe (chi : Real; f : Real) : Real;
const MaxError = 0.0001;
var
Bruch,
Summe,
Summand : Real;
k : longint;
begin
Summe := 1;
k := 1;
Bruch := 1;
repeat
Bruch := Bruch / (f + 2 * k);
Summand := power(chi, 2 * k) * Bruch;
Summe := Summe + Summand;
k := succ(k);
until (Summand < MaxError);
Reihe := Summe;
end;
类似的考虑应该应用于计算power(chi, 2*k)
,然后将其与改进的 Bruch 评估相结合。
编辑: 作为对您评论的回应,这里是基于幂函数属性的改进版本,即power(chi, 2*(k+1)) = power(chi, 2*k)*sqr(chi)
function Reihe (chi : Real; f : Real) : Real;
const MaxError = 0.0001;
var
chi2,
Summe,
Summand : Real;
k : longint;
begin
Summe := 1;
k := 1;
Summand := 1;
chi2 := sqr(chi);
repeat
Summand := Summand * chi2 / (f + 2 * k);
Summe := Summe + Summand;
k := succ(k);
until (Summand < MaxError);
Reihe := Summe;
end;