有效地计算nCk mod p

时间:2012-12-08 17:49:34

标签: c performance overflow combinatorics

我很多次遇到过这个问题,但我无法解决。会出现一些情况或其他会错误回答的情况,否则我写的程序会太慢。我正式谈论计算

nCk mod p其中p是素数n是一个大数,1< = k< = n。

我尝试了什么:

我知道factorial的递归公式,然后将其建模为动态编程问题,但我觉得它很慢。递归公式为(nCk) + (nCk-1) = (n+1Ck)。我在数组中存储值时处理模数以避免溢出,但我不确定只对结果执行mod p将避免所有溢出,因为可能需要删除它。

3 个答案:

答案 0 :(得分:0)

不确定“在数组中存储值”是什么意思,但我认为它们在运行时用作查找表以避免冗余计算以加快速度。这应该解决速度问题。关于溢出 - 您可以在计算的任何阶段执行模运算,并根据需要重复它 - 结果将是正确的。

答案 1 :(得分:0)

首先,让我们使用p相对较小的情况。 取n和k的base-p展开:写n = n_0 + n_1 p + n_2 p ^ 2 + ... + n_m p ^ m和k = k_0 + k_1 p + ... + k_m p ^ m其中每个n_i和每个k_i至少为0但小于p。一个定理(我认为是由于Edouard Lucas)说明C(n,k)= C(n_0,k_0)* C(n_1,k_1)* ... * C(n_m,k_m)。这减少了在下面的“n相对较小”的情况下采用数字的mod-p乘积。

其次,如果n相对较小,您可以使用公式C(n,k)= C(n-1,k-1)+ C(n-1,k)上的动态编程来计算二项式系数,在每一步减少mod p。或者做一些更聪明的事。

第三,如果k相对较小(并且小于p),你应该能够通过计算n!/(n-k)来计算n!/(k!(n-k)!)mod p!如n *(n-1)* ... *(n-k + 1),在每个乘积之后减去模p,然后乘以1和k之间的每个数的模数逆。

答案 2 :(得分:0)

要计算nCr,有一个基于规则nCr = (n - 1)C(r - 1) * n / r的简单算法:

def nCr(n,r):
  if r == 0:
    return 1
  return n * nCr(n - 1, r - 1) // r

现在在模运算中我们没有完全除法,但我们有模数逆(当用素数修改时)同样好

def nCrModP(n, r, p):
  if r == 0:
    return 1
  return n * nCrModP(n - 1, r - 1) * modinv(r, p) % p

这是rosettacode

上的一个implementation of modinv