算法学习|卢卡斯定理
卢卡斯定理
卢卡斯定理(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 $
算法步骤
- 递归分解:将 $ n $ 和 $ k $ 按 $ p $ 进制逐位分解
- 逐位计算:对每对 $ (n_i, k_i) $ 计算 $ C(n_i, k_i) \mod p $
- 累乘取模:将所有位的组合数结果相乘并取模
实现
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
关键点
- 素数条件:( p ) 必须为素数,否则定理不成立。
- 递归分解:通过 ( p ) 进制分解将大数组合数转化为小数组合数乘积。
- 逆元优化:使用费马小定理快速计算组合数的模逆元。
应用场景
- 密码学:大数组合数模运算(如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))

浙公网安备 33010602011771号