百度百科對於組合數的定義是:從n個不同元素中,任取m(m≤n)個元素併成一組,叫做從n個不同元素中取出m個元素的一個組合;從n個不同元素中取出m(m≤n)個元素的所有組合的個數,叫做從n個不同元素中取出m個元素的組合數。
由於經常遇到一些組合數問題,所以整理一些常見的快速求組合數的方法,附上Python的實現代碼。
一、m,n不是特別大的時候:
可以直接調用math.factorial求得階乘,然後算出組合數,如下:
import math
n,m = map(int,input().split())
print(math.factorial(n)//(math.factorial(m)*math.factorial(n-m)))
輸入:
5 3
輸出:
10
二、用定義式遞歸:
遞歸出口就在於當n=m或者m=1的時候。
n,m = map(int,input().split())
def rec(n,m):
if m == n:
return 1
elif m == 1:
return n
else:
return rec(n-1,m-1)+rec(n-1,m)
print(rec(n,m))
輸入:
10 3
輸出:
120
三、逆元+快速冪思想參考大佬
前面的兩種方法,在n,m數字很大的時候,運行時間會很長。在介紹第三個方法之前,先來介紹幾個概念,不當之處,歡迎指點。
(1)、同餘定理
百度百科:同餘定理數論中的重要概念。給定一個正整數m,如果兩個整數a和b滿足a-b能夠被m整除,即(a-b)/m得到一個整數,那麼就稱整數a與b對模m同餘,記作a≡b(mod m)。對模m同餘是整數的一個等價關係。
5 ≡ 3(mod 2) #5和 3對模2同餘
(2)、模的加減乘除運算
取模運算的等價變形適合加法、減法、乘法
但是,取模運算的等價變形不符合除法
比如:
(3)、逆元
逆元:對於a和p,若a和p互素且
則稱b爲a%p的逆元。
假設c爲b%p的逆元,即
這樣就把求轉化成一個乘法問題。
(4)、費馬小定理
百度百科:費馬小定理(Fermat’s little theorem)是數論中的一個重要定理,在1636年提出。如果p是一個質數,而整數a不是p的倍數,則有。
所以有
所以是的逆元。
由於m,n很大,現在要求的是,假設取100000007,由於取模運算的等價變形不適用於除法,即:
根據上面求得:
就相當於我們要求出的逆元,根據是的逆元,得到
所以
下面用代碼來實現:
import math
n,m = map(int,input().split())
p = 100000007
def power(x,y): #求x的y次方
p = 100000007
res = 1
while y:
if y % 2 != 0:
res *= (x%p)
y >>= 1
x *= (x%p)
return res
a = (math.factorial(n))%p
b = (power(math.factorial(m),(p-2)))%p
c = (power(math.factorial(n-m),(p-2)))%p
print(a*b*c%p)
中間的power函數是用快速冪做的,直接用Python的運算符“**”的話會非常大,會超時。
小白歡迎大佬指點~