关于组合数取模

本文探讨了组合数取模的计算方法,包括当n和m较小,可以用杨辉三角递推式解决;n和m较大且p为素数时,利用Lucas定理;当p不是素数时,借助中国剩余定理。详细阐述了每种情况下的计算步骤和技巧,如使用递归和周期性计算减少复杂度。

关于组合数取模

对于C(n,m) mod p (m<=n)

1、如果n,m比较,1<=m<=n<=1000,p<=1e9
可以用杨辉三角递推式去解决,用C[n][m]来储存C(n,m)的值,则递推式为:
C[i][j] mod p=(C[i-1][j-1]+C[i-1][j]) mod p(1<=j<=i) C[i][0]=1

2、如果n,m较大(1<=m<=n<=1e18)且p是素数
可以用Lucas定理去解决。

Lucas定理的定义:
如果有两个数n,m可以表示为
n=nk∗pk+nk−1∗pk−1+...+n1∗p1+n0n=n_k*p^k+n_{k-1}*p^{k-1}+...+n_1*p^1+n_0n=nkpk+nk1pk1+...+n1p1+n0
m=mk∗pk+mk−1∗pk−1+...+m1∗p1+m0m=m_k*p^k+m_{k-1}*p^{k-1}+...+m_1*p^1+m_0m=mkpk+mk1pk1+...+m1p1+m0
那么有
Cmn≡∏i=0kCmini(modp)C_{m}^{n}\equiv\prod_{i=0}^{k}C_{m_i}^{n_i} \pmod pCmni=0kCmini(modp)要证明Lucas定理,首先证明 Cpx≡0(modp)C_{p}^{x} \equiv 0 \pmod{p} Cpx0(modp)
证:
Cpx mod p=p!x!(p−x)! mod p=px∗(p−1)!(x−1)!(p−x)! mod p=p∗inv(x)∗Cp−1x−1 mod pC_{p}^{x}\bmod p=\frac{p!}{x!(p-x)!}\bmod p=\frac{p}{x}*\frac{(p-1)!}{(x-1)!(p-x)!}\bmod p=p*inv(x)*C_{p-1}^{x-1}\bmod p Cpxmodp=x!(px)!p!modp=xp(x1)!(px)!(p1)!modp=

组合数的算法及实现方法多样,以下是几种常见的算法及其实现: ### 杨辉三角法 杨辉三角法利用组合数的递推性质\(C(n,m)=C(n - 1,m)+C(n - 1,m - 1)\)来计算组合数。该方法适用于需要多次查询不同\(n\)和\(m\)组合数的情况,时间复杂度为\(O(n^2)\)。 ```python n = 1000 # 可根据实际需求调整 mod = 10**9 + 7 a = [[0] * (n + 1) for _ in range(n + 1)] for i in range(n + 1): for j in range(i + 1): if j == 0: a[i][j] = 1 else: a[i][j] = (a[i - 1][j - 1] + a[i - 1][j]) % mod # 计算 C(n, m) % mod n = 5 m = 2 result = a[n][m] print(result) ``` ### 拓展欧几里得求逆元法 拓展欧几里得算法用于求解\(ax + by = gcd(a,b)\)的整数解,当\(b\)为质数时,可用于求\(a\)在\(b\)意义下的逆元。组合数公式\(C(n,m)=\frac{n!}{m!(n - m)!}\),通过求\(m!(n - m)!\)在\(p\)意义下的逆元来计算组合数。 ```python def exgcd(a, b): if b == 0: return 1, 0, a x1, y1, d = exgcd(b, a % b) x = y1 y = x1 - (a // b) * y1 return x, y, d def mod_inverse(a, mod): x, _, d = exgcd(a, mod) if d != 1: raise ValueError("No inverse exists") return (x % mod + mod) % mod def factorial(n, mod): res = 1 for i in range(1, n + 1): res = (res * i) % mod return res n = 5 m = 2 mod = 10**9 + 7 numerator = factorial(n, mod) denominator = (factorial(m, mod) * factorial(n - m, mod)) % mod inverse = mod_inverse(denominator, mod) result = (numerator * inverse) % mod print(result) ``` ### 费马小定理求逆元法 费马小定理指出,若\(p\)是质数,\(a\)是整数且\(a\)与\(p\)互质,则\(a^{p - 1}\equiv1\pmod{p}\),那么\(a\)在\(p\)意义下的逆元为\(a^{p - 2}\)。可利用快速幂算法计算\(a^{p - 2}\)。 ```python def quick_pow(a, b, mod): res = 1 while b: if b & 1: res = (res * a) % mod a = (a * a) % mod b >>= 1 return res def mod_inverse_fermat(a, mod): return quick_pow(a, mod - 2, mod) n = 5 m = 2 mod = 10**9 + 7 numerator = factorial(n, mod) denominator = (factorial(m, mod) * factorial(n - m, mod)) % mod inverse = mod_inverse_fermat(denominator, mod) result = (numerator * inverse) % mod print(result) ``` ### 阶乘逆元递推法 通过递推的方式预先计算出阶乘的逆元,可在\(O(n)\)时间内完成预处理,后续每次查询组合数的时间复杂度为\(O(1)\)。 ```python n = 1000 # 可根据实际需求调整 mod = 10**9 + 7 fac = [1] * (n + 1) inv_fac = [1] * (n + 1) for i in range(1, n + 1): fac[i] = (fac[i - 1] * i) % mod inv_fac[n] = mod_inverse_fermat(fac[n], mod) for i in range(n - 1, 0, -1): inv_fac[i] = (inv_fac[i + 1] * (i + 1)) % mod n = 5 m = 2 result = (fac[n] * inv_fac[m] * inv_fac[n - m]) % mod print(result) ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值