如何计算 O(n) 中前 k 个二项式系数的总和而不会溢出?
Posted
技术标签:
【中文标题】如何计算 O(n) 中前 k 个二项式系数的总和而不会溢出?【英文标题】:How to compute sum of first k binomial coefficients in O(n) without overflow? 【发布时间】:2017-02-15 05:18:45 【问题描述】:我正在尝试在O(N)
中计算此值而不会溢出(使用 C++)
为了澄清,n
,r
是事先给出的,我试图在O(N)
中找到(n,r)
对的一个实例的答案
这是我的试验:
-
使用
O(N)
计算ans = n!/(r!(n-r)!2^n)
让c = ans
,用O(N)
修改c
:c /= (n-p); c*=(p+1)
为p = r-1 to 0
。为每个步骤添加c
到ans
基本上,我首先使用O(N)
计算最后一项,然后使用滑动窗口之类的东西来查找倒数第二个,然后是下一个……直到第一个。在这个过程中总结它们。
虽然看起来是正确的,但实际运行时间仍然比我预期的要慢。所以我想知道这个公式有什么特殊的已知技巧可以提高性能吗?如果没有,那么有没有办法减少常数因子? (基于以下sn-p)
另一个大问题是我面临一个困境:我不能单独计算 2^(-n)
或 nCr
大 n
,否则它会下溢/溢出。这就是为什么我尝试计算2^(-n) * The last term in the summation
和希望效果会相互抵消,这样我就不会在整个过程中出现下溢/溢出。有什么方法可以100%保证不会下溢/上溢?
(如果可能,我想避免使用大整数库)
// c++ code snippet to demonstrate the idea
double ans = 1;
for(int p=n; p>=1; p--)
ans *= p;
ans /= 2;
if(p <= r) ans /= p;
if(p <= n-r) ans /= p;
// now ans = n!/(r!(n-r)!2^n)
// use O(N) more time to find the ultimate ans: summation (n!/(r!(n-r)!2^n)) for r >= 0
double c = ans;
for(int p = r-1; p >= 0; p--)
c /= (n-p);
c *= (p+1);
// Each new c is the next term: n!/((r-1)!(n-r+1)!2^n)
ans += c;
【问题讨论】:
等等... sum 并没有说明 summation 变量是什么。如果n
和r
都是常量,而求和中的表达式是n choose r
,那么您似乎只是在对一个常量求和。
@user2357112 sum(nC0, nC1..nCr),这里的图不对,我去编辑一下
您使用double
是否意味着您对近似值感到满意?
@rici yes 如果近似值的精度至少为 10^-8...
mathoverflow.net/questions/17202/…
【参考方案1】:
计算并存储从 0 到 n 的每个 m 的 log(m!)。
计算对数(1/2**n)。
现在和的第 p 项是 exp(log(n!)-log(p!)-log((n-p)!)+log(1/2**n))。将这些术语加在一起。
【讨论】:
听起来不错的方法,试一试(虽然为了更容易实现,将使用base 2)【参考方案2】:为什么不在第一个循环中开始求和呢?如果第一个循环可以正确计算二项式系数,您可以将它们相加并计算分母。目前您只计算ans=2^(-n)
,因为其他操作相互取消。
写出的总和是
1/2^n*(1+n+n*(n-1)/2+n*(n-1)*(n-2)/(2*3)+...)
两个二项式之间的商是
nC[k+1]/nC[k] = (n-k) / (k+1)
另请注意,根据二项式定理,上索引 r
和 n-r-1
的部分和具有和 1
。
【讨论】:
如果我没有误会,您是说将ans
初始化为 2^(-n),然后使用一个循环进行求和,对吗?如果是这样的话,我这样做是因为我担心当 n 太大时, 2^(-n) 会下溢,所以我想将它与最大项 nCr
一起计算以“希望”它不会下溢也不会溢出
是的,那么您必须正确计算最大项。或者使用更大的n
部分二项式和的近似公式,请参阅密度和累积密度函数的伯努利分布和渐近近似。
+1 表示方程。问题是64位double
上的迭代步骤很快就会溢出(n=54,r=27)
以上n
的所有总和都是不好的,因为迭代步骤不再适合双精度并且将被截断......这导致重大错误。例如 n=100,r=50
是 double sum=684270969637826229800000000000/2^90
和 bigint sum=680189586700239570843455122823/(2^90)
所以只有前 2 位数字是正确的。以上是关于如何计算 O(n) 中前 k 个二项式系数的总和而不会溢出?的主要内容,如果未能解决你的问题,请参考以下文章