算法学习|卢卡斯定理


卢卡斯定理

卢卡斯定理(Lucas' Theorem) 计算大数的组合数模素数
$C(n, k)\mod p $,其中 $ p $ 是素数。


定理内容

将 $ n $ 和 $ k $ 表示为 $ p $ 进制数:
\(n = n_m p^m + n_{m-1} p^{m-1} + \dots + n_0\)
\(k = k_m p^m + k_{m-1} p^{m-1} + \dots + k_0\)
则有:
\(C(n, k) \equiv \prod_{i=0}^m C(n_i, k_i) \mod p\)
若存在某 $ k_i > n_i $,则 $ C(n, k) \equiv 0 \mod p $


算法步骤

  1. 递归分解:将 $ n $ 和 $ k $ 按 $ p $ 进制逐位分解
  2. 逐位计算:对每对 $ (n_i, k_i) $ 计算 $ C(n_i, k_i) \mod p $
  3. 累乘取模:将所有位的组合数结果相乘并取模

实现

def comb_mod(n, k, p):
    """计算 C(n, k) mod p,其中 n 和 k 均小于 p"""
    if k > n:
        return 0
    # 分子与分母的乘积取模
    numerator = 1
    denominator = 1
    for i in range(k):
        numerator = numerator * (n - i) % p
        denominator = denominator * (i + 1) % p
    # 费马小定理求逆元
    return numerator * pow(denominator, p-2, p) % p

def lucas(n, k, p):
    """递归应用卢卡斯定理计算 C(n, k) mod p"""
    if k == 0:
        return 1
    ni = n % p
    ki = k % p
    if ki > ni:
        return 0
    # 递归计算高位组合数,并与当前位结果相乘
    return (lucas(n // p, k // p, p) * comb_mod(ni, ki, p)) % p

关键点

  1. 素数条件:( p ) 必须为素数,否则定理不成立。
  2. 递归分解:通过 ( p ) 进制分解将大数组合数转化为小数组合数乘积。
  3. 逆元优化:使用费马小定理快速计算组合数的模逆元。

应用场景

  • 密码学:大数组合数模运算(如RSA算法中的参数生成)。
  • 竞赛算法:快速求解组合数取模问题(如动态规划中的状态转移)。

#https://www.lanqiao.cn/problems/1159/learning/
def inv(x,p):
    return pow(x,p-2,p)
def comb(n,m,p):
    if m > n:
        return 0
    res = 1
    for i in range(m):
        res = res*(n-i)*inv(i+1,p) % p
    return res
def lucas(n,m,p):
    if m == 0:
        return 1
    return comb(n%p,m%p,p)*lucas(n//p,m//p,p)%p
t = int(input())
for i in range(t):
    n,m,p = map(int,input().split())
    print(lucas(n,m,p))
posted @ 2025-04-10 11:52  lumiere_cloud  阅读(111)  评论(1)    收藏  举报