組合數(shù)
取模就是求
的值,根據(jù)
,
和
的取值范圍不同,采取的方法也不一樣。
下面,我們來(lái)看常見的兩種取值情況(m、n在64位整數(shù)型范圍內(nèi))
(1)
? ,
???? 此時(shí)較簡(jiǎn)單,在O(n 2 )可承受的情況下組合數(shù)的計(jì)算可以直接用楊輝三角遞推,邊做加法邊取模。
(2)
, ?
,并且
是素?cái)?shù)
? 本文針對(duì)該取值范圍較大又不太大的情況(2)進(jìn)行討論。
???? 這個(gè)問題可以使用 Lucas 定理,定理描述:
其中
????
???? 這樣將組合數(shù)的求解分解為小問題的乘積,下面考慮計(jì)算 C(ni, mi) %p .
已知C(n, m) mod p = n!/(m!(n - m)!) mod p。當(dāng)我們要求(a/b)mod p的值,且a很大,無(wú)法直接求得a/b的值時(shí),我們可以轉(zhuǎn)而使用乘法逆元k,將a乘上k再模p,即(a*k) mod p。 其結(jié)果與(a/b) mod p等價(jià)。
那么逆元是什么?
定義:滿足a*k≡1 (mod p)的k值就是a關(guān)于p的乘法
逆元
(當(dāng)p是1時(shí),對(duì)于任意a,k都為1)
除法取模,這里要用到m!(n-m)!的逆元。
根據(jù) 費(fèi)馬小定理 :
已知gcd(a, p) = 1,則 a p-1 ?≡ 1 (mod p), ?所以 a*a p-2 ?≡ 1 (mod p)。
也就是 (m!(n-m)!)的逆元為 (m!(n-m)!) p-2 ;
下面附上Lucas定理的一種證明,見下圖,參考馮志剛《 初等數(shù)論 》第37頁(yè)。
題意:
求
,其中
,并且
是素?cái)?shù)。
代碼:

#include<iostream>
//
#include<algorithm>
using
namespace
std; typedef
long
long
ll;
int
quick_power_mod(
int
a,
int
b,
int
m){
//
pow(a,b)%m
int
result =
1
;
int
base
=
a;
while
(b>
0
){
if
(b &
1
==
1
){ result
= (result*
base
) %
m; }
base
= (
base
*
base
) %
m; b
>>=
1
; }
return
result; }
//
計(jì)算組合數(shù)取模
ll comp(ll a, ll b,
int
p) {
//
composite num C(a,b)%p
if
(a < b)
return
0
;
if
(a == b)
return
1
;
if
(b > a - b) b = a -
b;
int
ans =
1
, ca =
1
, cb =
1
;
for
(ll i =
0
; i < b; ++
i) { ca
= (ca * (a - i))%
p; cb
= (cb * (b - i))%
p; } ans
= (ca*quick_power_mod(cb, p -
2
, p)) %
p;
return
ans; } ll lucas(ll n, ll m, ll p) { ll ans
=
1
;
while
(n&&m&&
ans) { ans
= (ans*comp(n%p, m%p, p)) % p;
//
also can be recusive
n /=
p; m
/=
p; }
return
ans; }
int
main(){ ll m,n;
while
(cin>>n>>
m){ cout
<<lucas(n,m,
10007
)<<
endl; }
return
0
; }
? 上面的代碼中用到了求冪取模操作來(lái)計(jì)算(m!(n-m)!) p-2 % p.下面解釋冪取模算法:
反復(fù)平方法 求a b %m
通過研究指數(shù)b的二進(jìn)制表示發(fā)現(xiàn),對(duì)任意的整數(shù)b都可表示為:
- n表示b的實(shí)際二進(jìn)制位數(shù)
- b i 表示該位是0或1
因此,a b 可表示為:
即用b的每一位表示a的每一項(xiàng),而對(duì)任意相鄰的兩項(xiàng)存在平方關(guān)系,即:
因此我們構(gòu)造下面的算法:
- 把b轉(zhuǎn)換為二進(jìn)制表示,并從右至左掃描其每一位(從低到高)
-
當(dāng)掃描到第i位時(shí),根據(jù)同余性質(zhì)(2)計(jì)算a的第i項(xiàng)的模:
base變量表示第i-1位時(shí)計(jì)算出的模,通過遞歸能很容易地確定所有位的模。
- 如果第i位為1,即b i =1,則表示該位需要參與模運(yùn)算,計(jì)算結(jié)果 result = (result*base) mod m;其中result為前i-1次的計(jì)算結(jié)果;若b i =0,則表示a的第i項(xiàng)為1,不必參與模運(yùn)算
int quick_power_mod(int a,int b,int m){
int result = 1;
int base = a;
while(b>0){
if(b & 1==1){
result = (result*base) % m;
}
base = (base*base) %m;
b >>=1;
}
return result;
}
其中運(yùn)用了兩個(gè)同余性質(zhì):
同余性質(zhì)1:ab≡bc (mod m)
同余性質(zhì)2: ? a≡c (mod m) => a 2 ≡c 2 (mod m)
理解要點(diǎn):
- base記錄了a的每項(xiàng)的模,無(wú)論b在該位是0還是1,該結(jié)果都記錄,目的是給后續(xù)位為1的項(xiàng)使用,計(jì)算方式是前一結(jié)果的平方取模,這也是反復(fù)平方法的由來(lái)
- result只記錄了位為1的項(xiàng)的模結(jié)果,該計(jì)算方式使用了同余性質(zhì)1
- 通過地把a(bǔ)使用二進(jìn)制表示,并結(jié)合同余性質(zhì)1,2,巧妙地化解了大數(shù)取模的運(yùn)算。對(duì)1024位這樣的大數(shù),也最多進(jìn)行1024次循環(huán)便可計(jì)算模值,性能非常快。
該方法是許多西方數(shù)學(xué)家努力的結(jié)果,通常也稱為Montgomery算法。
(以上部分內(nèi)容由網(wǎng)絡(luò)搜集整理而來(lái),不當(dāng)之處,煩請(qǐng)不吝賜教)
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
