📅  最后修改于: 2023-12-03 14:55:32.175000             🧑  作者: Mango
在组合数的计算中,经常需要判断组合数的值是否能被给定的素数整除。这个问题可以通过计算n!与r!(n-r)!的乘积,再求其在给定素数下的剩余系数来解决。
我们可以利用Fermat小定理,即如果p为素数,则对于任何整数a,以下恒成立:
$$ a^{p-1} \equiv 1 \pmod{p} $$
根据上式,如果我们要求C(n,r)模p的余数,可以使用下面的公式:
$$ \binom{n}{r} = \frac{n!}{r!(n-r)!} \equiv \frac{\prod_{i=r+1}^n i}{\prod_{i=1}^{n-r} i} \pmod{p} $$
由于p是素数,因此根据费马小定理,对于1<=i<=n-r,i与p互质,因此有:
$$ \prod_{i=1}^{n-r} i \equiv (n-r)! \pmod{p} $$
因此,有以下式子:
$$ \binom{n}{r} \equiv \frac{\prod_{i=r+1}^n i}{(n-r)!} \pmod{p} $$
Fermat小定理只能用于p是素数的情况。对于p不是素数的情况,我们可以使用Lucas定理。Lucas定理是Fermat小定理在扩展到组合数的情况下的一种推广。
对于给定的n和r,设p的十进制表示中有k位,即$p=\sum_{i=0}^{k-1} d_i \times 10^i$,则有以下Lucas定理:
$$ \binom{n}{r} \equiv \binom{n_0}{r_0} \binom{n_1}{r_1} \cdots \binom{n_{k-1}}{r_{k-1}} \pmod{p} $$
其中,$n=n_0+n_1\times 10+n_2 \times 10^2 + \cdots + n_{k-1} \times 10^{k-1}$,$r=r_0+r_1\times 10+r_2 \times 10^2 + \cdots + r_{k-1} \times 10^{k-1}$。
通过Lucas定理,我们可以将组合数的计算转化为多个小范围内组合数的计算。对于一个范围内的组合数,可以使用预处理的方法提前算出来。
def fermat_comb(n, r, p):
if r > n:
return 0
num = 1
den = 1
for i in range(n-r+1, n+1):
num = (num * i) % p
for i in range(1, r+1):
den = (den * i) % p
den_inv = pow(den, p-2, p)
return (num * den_inv) % p
def factorials_mod_p(n, p):
factorials = [1]
for i in range(1, n+1):
factorials.append((factorials[-1] * i) % p)
return factorials
def binom_mod_p(n, r, p, factorials=None):
if r > n:
return 0
if not factorials:
factorials = factorials_mod_p(n, p)
num = factorials[n]
den = (factorials[r] * factorials[n-r]) % p
den_inv = pow(den, p-2, p)
return (num * den_inv) % p
def lucas_comb(n, r, p):
if r > n:
return 0
if n < p:
return binom_mod_p(n, r, p)
res = 1
while n and r and res:
n_cur = n % p
r_cur = r % p
if n_cur < r_cur:
return 0
res = (res * binom_mod_p(n_cur, r_cur, p)) % p
n //= p
r //= p
return res
在计算组合数时,如果要求组合数模素数p的余数,可以利用费马小定理或Lucas定理,将组合数的计算转化为小范围内组合数的计算,以此避免大数的计算。