【问题标题】:How can we compute N choose K modulus a prime number without overflow?我们如何计算 N 选择 K 模一个素数而不溢出?
【发布时间】:2011-06-06 01:20:47
【问题描述】:

我们如何在 C 或 C++ 中计算 (N choose K)% M 而不调用溢出?

对于 N (4 和 K (1 和 M = 1000003 的特殊情况

【问题讨论】:

标签: c++ c algorithm math modulo


【解决方案1】:

要计算 (n 选择 k) % M,您可以分别计算分母 (n!) 模 M 和分母 (k!*(n - k)!) 模 M,然后将分母乘以分母的模乘法逆(以 M 为单位)。由于 M 是素数,您可以使用费马小定理计算乘法逆。

在以下链接(问题 SuperSum)上有一个很好的解释和示例代码:

http://www.topcoder.com/wiki/display/tc/SRM+467

【讨论】:

  • 为了获得额外的速度,将分子计算为 (K+1) 到 N 的乘积,将分母计算为 K!。我们知道在计算中不会有 M 的任何因数,因为它是素数并且大于 N。因此我们可以取消顶部和底部,而不必担心我们要取消的可能是 M 的倍数(即 0)。
  • 但现在我看到 1,000,000,003 不是素数,知道现在如何解决这个问题吗?
  • @Tretwick Marian:除非它因某种原因被禁止,否则只要拿出一个 bignum 库 (GMP) 并做显而易见的事情。 1000!/500!具有少于 5k 的二进制数字,这可能不会太大,无法进行大约 1000 次算术运算。如果你需要优化,你最坏的情况是 1000 选择 500,它只有 1k 个二进制数字。所以要聪明地计算它,尽快做除法而不是先做所有的乘法,而且数字永远不会比这大得多。
【解决方案2】:

由于 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

【讨论】:

【解决方案3】:

您可以使用您提供的链接中的递归公式并进行计算 mod M。

【讨论】:

    【解决方案4】:

    这是一个简单的例子:

    (A * B * C) % N ... is equal to... ((A % N) *  (B % N) * (C % N)) % N;
    

    也就是说,您只需要对每个操作数和乘积应用模数,或者只要它变成一个大数。最后,模数必须适用于整体结果。

    【讨论】:

    • 使用 32 位 int 仍然可能导致 1000000000*1000 溢出。
    • @ybungalobill : 申请 ((1000000000%N)*(1000)%N)%N.
    • 请注意,问题中的模数是 M,而不是 N,给出的示例虽然实际上不是素数,但大约是十亿。所以1000000000%N 仍然是1000000000。为避免溢出,您需要一个与 N^2 一样大的整数类型(例如 long longint64_t,如果可用)
    【解决方案5】:

    使用Stirling's approximation 计算二项式系数。然后像往常一样计算模数。

    【讨论】:

    • 近似值如何帮助计算模 M 的二项式系数?近似值在模算术中几乎没有意义。
    • 对不起,我错过了近似部分。
    猜你喜欢
    • 1970-01-01
    • 2013-02-24
    • 2020-07-18
    • 1970-01-01
    • 2012-06-23
    • 1970-01-01
    • 2018-10-10
    • 1970-01-01
    • 2013-01-17
    相关资源
    最近更新 更多