盧卡斯定理證明

在開始介紹盧卡斯定理之前我們先介紹下面的3個定理
1.乘法逆元
	如果ax≡1 (mod p),gcd(a,p)=1(a與p互質),則稱a關於模p的乘法逆元爲 x
	ax≡1 (mod p) 這個等式用中文描述就是 a乘一個數x並模p等於1,即 a%p*x%p=res,res%p=1

2.費馬小定理:如果p是一個質數,而整數a不是p的倍數,即這兩個數互質,則有
	a^(p - 1)1 mod(p)	gcd(a, p) = 1

3.拓展歐幾里得
	已知整數a、b,擴展歐幾里得算法可以在求得a、b的最大公約數的同時,
	能找到整數x、y(其中一個很可能是負數),使它們滿足貝祖等式ax + by = gcd(a, b)


problem_1:爲什麼我們可以用費馬小定理來求得逆元?
	證明:	由費馬小定理	a^(p - 1)1 mod(p) 變形得 ->>	a * a^(p - 2)1 mod(p)
			因爲 a, p 互質,a * a^(p - 2)1 mod(p) 且 ax≡1 (mod p) 則 x = a^(p - 2) mod(p)

problem_2:爲什麼我們可以用拓展歐幾里得來求得逆元?
	證明:1		我們都知道模後的數就是餘數,比如12%5=12-5*2=218%4=18-4*4=2。(/是程序運算中的除)
				那麼ax≡1 (mod p)即ax-yp=1.把y寫成+的形式就是ax+py=1,爲方便理解下面我們把p寫成b就是ax+by=1。
				就表示x是a的模b乘法逆元,y是b的模a乘法逆元。然後就可以用擴展歐幾里得求了

        法2		那麼怎麼由擴展歐幾里得算法求逆元呢?
                很簡單~
                使用條件: a,b爲正整數,而且gcd(a,b) = 1
                證明:
                因爲a,b 互質,所以一定有 ax+by = 1
                兩邊同時對b 取餘
                ax%b + by %b = 1%b   ------->   ax%b = 1%b
                即 ax ≡ 1 (mod b)
                擴展歐幾里得中x 就是a關於b的逆元
                同理y 就是 b 關於 a的逆元
                所以使用完歐幾里得算法,我們判斷返回值d 是否爲1,爲1說明 gcd(a,b) = 1
                即求得的x就是 a關於b的逆元

problem_3:這個逆元有啥子用?
		  乘法逆元最大的作用就是,在要除以一個數,再取模時,把除法變成乘法運算,然後再取模。
		  因爲除法,比如用16/5應該是3.2,但是計算機會算成3.。。誤差有沒有,用double就更不用說了,
		  數大了一定有誤差,所以,有了逆元!!!!
		  (a  /  b) % p =  (a * inv(b) ) % p = (a % p * inv(b) % p) % p 	// inv代表的是逆元

		  例如:12 / 5 mod 7 = ?
		  gcd(g, 7) = 1------->>>>>>> inv(v) = 3
		  ----->>>>>> (12 * 3) % 7 = 1

好了,在瞭解了上面的知識後,我們進入正題,看一看這個盧卡斯到底什麼東西.

定理描述:

		   k
C(n, m) =C(ni, mi) (mod p)
		  i=0

Lucas定理是用來求 c(n,m) mod p,p爲素數的值。

證明: C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p

C(n, m) mod p = n!/(m!(n - m)!) mod p
當我們要求(a/b)mod p的值,且b很大,無法直接求得a/b的值時,
我們可以轉而使用乘法逆元k,將a乘上k再模p,即(a*k) mod p。 其結果與(a/b) mod p等價。

也就是Lucas(n,m)%p=Lucas(n/p,m/p)*C(n%p,m%p)%p

注意:C(N, 0, P) = 1

例題:洛谷: 3807.
Ac 代碼:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;

ll fac[maxn];
ll n, m, mod;

void init()
{
	fac[0] = 1;
	for(int i = 1; i <= mod; ++i)
		fac[i] = (i * fac[i - 1]) % mod;    // 先把mod內的階乘算出來, 進行預處理
}

ll kpow(ll a, ll b) // 快速冪
{
	ll ans = 1;
	while(b){
		if( b & 1) ans = (ans * a) % mod;
		a = (a * a) % mod;
		b>>=1;
	}
	return ans;
}

ll inv(ll x)    // 求逆元
{
	return kpow(x, mod - 2);
}

ll C(ll n, ll m)    // 
{
	if(n < m) return 0;
	return (fac[n] * inv(fac[m] * fac[n - m])) % mod;
}

ll Lucas(ll k, ll b)    // 一直進行遞歸, 直到 進行到這裏 -->>>  C(N, 0, P) = 1
{
	if(b == 0) return 1;
	return C(k%mod, b%mod) * Lucas(k/mod, b/mod)% mod;
}

int main(void)
{
	int t;
	cin>>t;
	while(t--){
		scanf("%lld%lld%lld", &n, &m, &mod);
		init();
        printf("%lld\n", Lucas(n, m));
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章