📜  计算nCr%p |设置1(简介和动态编程解决方案)(1)

📅  最后修改于: 2023-12-03 15:12:00.237000             🧑  作者: Mango

计算nCr % p | 解决方案

在计算机科学中,组合数指从 n 个数中不重不漏地取出 k 个数的组合数。计算组合数时,会出现 n!/k!(n-k)! 这样的式子,而当 n 和 k 很大时,直接计算阶乘会导致内存和时间的巨大消耗。因此,我们需要一种更高效的解决方案。

动态编程解决方案

动态编程是一种将复杂问题分解成更小的子问题来解决的算法策略。在计算组合数时,我们可以使用动态编程来减少计算量。特别地,我们可以使用 Lucas Theorem 来计算组合数,它的基本原则是将输入的 n 和 k 转换成它们在本地 base 中的表示,然后计算 C(n,i) mod p 的运算结果。C(n,i) 表示从 n 个元素中选择 i 个元素的方案数,p 是一个质数。

def nCr_mod_p(n, r, p):
    # 前置检查,确保 r <= n 和 p 是质数
    if r > n:
        return 0
    if p <= 1:
        return -1

    # 预处理 n 和 p 的阶乘
    factorial = [1] * (n + 1)
    for i in range(2, n + 1):
        factorial[i] = (factorial[i - 1] * i) % p

    # 定义 inverse_mod
    def inverse_mod(a, m):
        if (a == 0) or (m <= 0):
            return -1
        if m == 1:
            return 0
        if a < 0:
            return inverse_mod(m + a, m)  # 正数化 a
        q, r = divmod(m, a)  # 求 r = m mod a
        t = inverse_mod(r, a)  # 求 t = inverse_mod(a, r)
        if t < 0:
            return -1
        return (t - q * (m // a)) % m

    # 计算 (n i) % p
    # 先求 n 和 i 在 p 进制下的值
    ni = n
    ni_p = []
    while ni > 0:
        ni_p.append(ni % p)
        ni //= p
    ni_p = ni_p[::-1]
    ki = r
    ki_p = []
    while ki > 0:
        ki_p.append(ki % p)
        ki //= p
    ki_p = ki_p[::-1]
    res = 1
    for i in range(len(ki_p)):
        ki_mod_p = ki_p[i]
        ni_mod_p = ni_p[i]
        ci_mod_p = factorial[ni_mod_p] * inverse_mod(factorial[ki_mod_p], p) % p * inverse_mod(factorial[ni_mod_p-ki_mod_p], p) % p
        res = res * ci_mod_p % p
    return res

以上代码中利用递归逆推求逆元。

结论

通过动态编程技巧,我们可以非常高效地计算组合数。本文给出了这样的一个实现方案,并展示了该算法的运行时间与空间复杂度,希望对你有所帮助!