我必须计算表达式的c二项式系数 (x + y)** n,n非常大(500-1000的数量级)。我想到的计算二项式系数的第一个算法是multiplicative formula。所以我将它编码为我的程序
long double binomial(int k, int m)
{
int i,j;
long double num=1, den=1;
j=m<(k-m)?m:(k-m);
for(i=1;i<=j;i++)
{
num*=(k+1-i);
den*=i;
}
return num/den;
}
这个代码在单个核心线程上非常快,例如与recursive formula进行比较,尽管后者不太容易出现舍入错误,因为只涉及求和而不是除法。 因此,我想测试这些算法以获得很好的价值并尝试评估500选择250(订购10 ^ 160)。我发现&#34;相对错误&#34;小于10 ^( - 19),所以基本上它们是相同的数字,虽然它们有10 ^ 141的不同。
所以我想知道:有没有办法评估计算错误的顺序?是否有一些快速的方法来计算二项式系数,这比乘法公式更精确?由于我不知道算法的准确性,因此我不知道在哪里截断斯特林系列以获得更好的结果。
我已经搜索了一些二项式系数表,所以我可以从这些表中复制,但我发现的最好的一个在n = 100时停止......
答案 0 :(得分:1)
要获得小k
和m
的精确整数结果,可能会有更好的解决方案(代码略有不同):
unsigned long binomial(int k, int m)
{
int i,j; unsigned long num=1;
j=m<(k-m)?m:(k-m);
for(i=1;i<=j;i++)
{
num*=(k+1-i);
num/=i;
}
return num;
}
每次在进行除法num/=i
后获得组合数字时,您都不会被截断。要获得较大k
和m
的近似结果,您的解决方案可能会很好。但要注意long double
乘法已经比整数的乘法和除法慢得多(unsigned long
或size_t
)。如果你想要更精确地获得更大的数字,可能必须对一个大整数class
进行编码或包含在库中。如果有n!
极大整数n
的快速因子算法,您也可以谷歌搜索。这对组合学也有帮助。当ln(n!)
很大时,斯特林公式是n
的良好近似值。这一切都取决于你想要的准确程度。
答案 1 :(得分:1)
如果你真的想使用乘法公式,我建议采用基于异常的方法。
这是一个骨架代码,可以给你一个想法:
long long binomial_l(int k, int m)
{
int i,j;
long long num=1, den=1;
j=m<(k-m)?m:(k-m);
for(i=1;i<=j;i++)
{
int multiplier=(k+1-i);
int divisor=i;
long long candidate_num=num*multiplier;
//check multiplication
if((candidate_num/multiplier)!=num)
{
//resolve exception...
}
else
{
num=candidate_num;
}
candidate_num=num/divisor;
//check division
if((candidate_num*divisor)==num)
{
num=candidate_num;
}
else
{
//resolve exception
den*=divisor;
//this multiplication should also be checked...
}
}
long long candidate_result= num/den;
if((candidate_result*den)==num)
{
return candidate_result;
}
// you should not get here if all exceptions are resolved
return 0;
}
答案 2 :(得分:1)
如果您只计算单个二项式系数C(n,k)n
相当大但不大于约1750,那么使用一个像样的C库最好的方法是使用{{3}标准库函数:
tgammal(n+1) / (tgammal(n-k+1) * tgammal(k+1))
使用libm的Gnu实现进行测试,该结果始终在精确值的几个ULP内产生结果,并且通常优于基于乘法和除法的解决方案。
如果k
足够小(或大),二项式系数不会溢出64位精度,那么你可以通过交替乘法和除法得到精确的结果。
如果n
如此之大以至于tgammal(n+1)
超出了长双倍(超过1754)的范围,但又没有那么大以至于分子溢出,那么乘法解是最好的一个bignum图书馆。但是,您也可以使用
expl(lgammal(n+1) - lgammal(n-k+1) - lgammal(k+1))
不太精确但更容易编码。 (此外,如果系数的对数对您有用,则上述公式将适用于相当大的n和k范围。不必使用expl
将提高准确性。)
如果您需要一系列具有相同n
值的二项式系数,那么您最好的选择是迭代加法:
void binoms(unsigned n, long double* res) {
// res must have (n+3)/2 elements
res[0] = 1;
for (unsigned i = 2, half = 0; i <= n; ++i) {
res[half + 1] = res[half] * 2;
for (int k = half; k > 0; --k)
res[k] += res[k-1];
if (i % 2 == 0)
++half;
}
}
以上仅产生k从0到n / 2的系数。它具有比乘法算法略大的舍入误差(至少当k接近n / 2时),但如果你需要所有系数并且它具有更大范围的可接受输入,它会快得多。
答案 3 :(得分:0)
这可能不是OP正在寻找的,但是可以通过二元熵函数分析近似nCr的大n。它在
中提到