【发布时间】:2011-06-06 01:20:47
【问题描述】:
我们如何在 C 或 C++ 中计算 (N choose K)% M 而不调用溢出?
对于 N (4 和 K (1 和 M = 1000003 的特殊情况。
【问题讨论】:
标签: c++ c algorithm math modulo
我们如何在 C 或 C++ 中计算 (N choose K)% M 而不调用溢出?
对于 N (4 和 K (1 和 M = 1000003 的特殊情况。
【问题讨论】:
标签: c++ c algorithm math modulo
要计算 (n 选择 k) % M,您可以分别计算分母 (n!) 模 M 和分母 (k!*(n - k)!) 模 M,然后将分母乘以分母的模乘法逆(以 M 为单位)。由于 M 是素数,您可以使用费马小定理计算乘法逆。
在以下链接(问题 SuperSum)上有一个很好的解释和示例代码:
【讨论】:
由于 1000000003 = 23 * 307 * 141623,您可以计算 (n 选择 k) mod 23、307 和 141623,然后应用中国提醒定理 [1]。计算 n! 时,k!和 (n-k)!,你应该计算每一步的一切 mod 23、307 和 141623 以防止溢出。
通过这种方式,即使在 32 位机器中也应该避免溢出。
一点改进是计算 (n 选择 k) mod 141623 和 7061 (23 * 307)(编辑:但计算反模数 7061 可能有点棘手,所以我不会这样做)
对不起,我的英语不好。
[1]http://en.wikipedia.org/wiki/Chinese_remainder_theorem
Edit2:我发现的另一个潜在问题是在计算 n 时! mod 23(例如)它可能是 0,但这并不意味着(n 选择 k)是 0 mod 23,所以你应该计算 23 除以 n 的次数!,(n-k)!和k!在计算之前(n 选择 k)。计算这很容易,p 除以 n!正好是 floor(n/p) + floor(n/p²) + ... 次。如果发生 23 除以 n!它除 k 的时间相同!和 (n-k)!,您继续计算 (n 选择 k) mod 23 除以 23 的每个倍数。这同样适用于 307,但不适用于 141623
【讨论】:
您可以使用您提供的链接中的递归公式并进行计算 mod M。
【讨论】:
这是一个简单的例子:
(A * B * C) % N ... is equal to... ((A % N) * (B % N) * (C % N)) % N;
也就是说,您只需要对每个操作数和乘积应用模数,或者只要它变成一个大数。最后,模数必须适用于整体结果。
【讨论】:
((1000000000%N)*(1000)%N)%N.
1000000000%N 仍然是1000000000。为避免溢出,您需要一个与 N^2 一样大的整数类型(例如 long long 或 int64_t,如果可用)
使用Stirling's approximation 计算二项式系数。然后像往常一样计算模数。
【讨论】: