Lucas定理(大组合数取模)
一、定义:
当n、m为大数,p为素数时,
Lucas定理是用来求 c(n,m) mod p的
值。
适用领域范围
:
在数论中求大组合数取模。
表达式:
C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p
二、定理内容:
Lucas定理:我们令
n=sp+q , m=tp+r .
(q ,r ≤p)
那么:
代码可以递归的去完成这个过程,其中递归终点为
t = 0 ;
时间
O(logp(n)*p))。
三、证明及推导: (该过程边看边写效果更佳!)
要证:C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p
即证:C(n,m)=C(n/p,m/p)*C(n%p,m%p)
证明:
已知p是素数,n、m、p为整数。
1.由二项式定理得:
,且
2.由费马小定理得:
(1)式 :(1+x)^p ≡ (1+x)(mod p).
(2)式 :(1+x^p)≡ (1+x)(mod p). (因为x^p与x取模p同余,同时加1也同余
)
则由上式可知:
(1+x)^p ≡ (1+x^p)(mod p) (记为(3)式)
3.n = n/p*p + n%p . 则:
(1+x)^p(mod p) = (1+x)^(n/p*p)*(1+x)^(n%p)(mod p)
由式3得: (1+x)^p(mod p)= (1+x^p)^(n/p)*(1+x)^(n%p)(mod p)
将上式由二项式公式可转化为:(这一排你用手写看一下吧,这编辑器里太难敲了……)
∑(n,z=0)C(n,z)* x^z (mod p)=∑(n/p,i=0)C(n/p,i)* x^(p*i)*∑(n%p,j=0)C(n%p,j)* x^j(
mod p)
任意一个Z,必存在一个i,j满足:x^z = x^(p*i)*x^j(即满足:n = n/p*p + n%p),当且仅当:
i=z/p,j=z%p
时成立
此时:
C(n,m)=C(n/p,m/p)*C(n%p,m%p) 成立
得证!
代码求解过程中,不断将C(n/p,m/p)拆分化简,实质是一直在计算C(n%p,m%p),即:
C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p
等价于C(n,m)%p=C(n/p/p,m/p/p)*C(n/p%p,m/p%p)%p
等价于C(n,m)%p=C(n/p/p/p,m/p/p/p)*C(n/p/p%p,m/p/p%p)%p
等价于…
直到拆分至m=0,递归完成。
四、逻辑代码:
#include
using namespace std;
typedef long long ll;
ll fastmod(ll a,ll b,ll p) //费马小定理
{
ll cnt=1;
while(b)
{
if(b&1)
cnt=(cnt*a)%p;
a=(a*a)%p;
b>>=1;
}
return cnt;
}
ll Comb(ll a,ll b,ll p) //组合数
{
ll ca=1,cb=1,tmp=1;;
if(aa-b) b=a-b;
for(ll i=0;i>n>>m>>p)
{
ll ans=Lucas(n,m,p);
cout<
~step by step