正如 Shane 已经正确指出的那样,该方程是正常 cdf 的泰勒展开的实现。该sum
值在“真实”值的上下迭代,精度越来越高。如果该值接近 1 或 0,则存在一个非常低但存在的概率,即sum
>1 或 <0,因为(相对)较早的突破loopstop
。1/Math.sqrt(2*Math.Pi)
通过舍入0.3989422804
和javascript浮点数的精度问题进一步加强了偏差。此外,提供的解决方案不适用于 z 分数 >7 或 <-7
我使用decimal.js npm 库更新了代码以更准确,并直接返回 p 值:
function GetpValueFromZ(_z, type = "twosided")
{
if(_z < -14)
{
_z = -14
}
else if(_z > 14)
{
_z = 14
}
Decimal.set({precision: 100});
let z = new Decimal(_z);
var sum = new Decimal(0);
var term = new Decimal(1);
var k = new Decimal(0);
var loopstop = new Decimal("10E-50");
var minusone = new Decimal(-1);
var two = new Decimal(2);
let pi = new Decimal("3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647")
while(term.abs().greaterThan(loopstop))
{
term = new Decimal(1)
for (let i = 1; i <= k; i++) {
term = term.times(z).times(z.dividedBy(two.times(i)))
}
term = term.times(minusone.toPower(k)).dividedBy(k.times(2).plus(1))
sum = sum.plus(term);
k = k.plus(1);
}
sum = sum.times(z).dividedBy(two.times(pi).sqrt()).plus(0.5);
if(sum.lessThan(0))
sum = sum.abs();
else if(sum.greaterThan(1))
sum = two.minus(sum);
switch (type) {
case "left":
return parseFloat(sum.toExponential(40));
case "right":
return parseFloat((new Decimal(1).minus(sum)).toExponential(40));
case "twosided":
return sum.lessThan(0.5)? parseFloat(sum.times(two).toExponential(40)) : parseFloat((new Decimal(1).minus(sum).times(two)).toExponential(40))
}
}
通过增加 Decimal.jsprecision
值并减少该loopstop
值,您可以获得非常小(或非常高)z 分数的准确 p 值,以计算时间成本。