我很多次遇到过这个问题,但我无法解决。会出现一些情况或其他会错误回答的情况,否则我写的程序会太慢。我正式谈论计算
nCk mod p
其中p是素数n是一个大数,1< = k< = n。
我尝试了什么:
我知道factorial的递归公式,然后将其建模为动态编程问题,但我觉得它很慢。递归公式为(nCk) + (nCk-1) = (n+1Ck)
。我在数组中存储值时处理模数以避免溢出,但我不确定只对结果执行mod p
将避免所有溢出,因为可能需要删除它。
答案 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