卢卡斯定理
最近接触到的一个大组合数求余方法,个人觉得推导过程比较晦涩难懂,但是在组合数比较大的时候性能的确是优秀,所以这里记录一下。卢卡斯定理的推导式如下(以如既往的铅笔加手写~剩下码公式的时间去板砖了我。。。):
python实现
这里给出一个递推的python实现方法
class cmnModP:
def __init__(self,p):
self.p=p
self.fc=[1]
for i in range(1,p+1):
self.fc.append(i*self.fc[-1])
def C(self,m,n):
if m==n:return 1
if m<n:return 0
return self.fc[m]/self.fc[n]/self.fc[m-n]
def lucas(self,m,n):
if n==0:return 1
return self.lucas(m//self.p,n//self.p)*self.C(m%self.p,n%self.p)%self.p
与传统方法的性能对比
我们用传统意义上的求余方法如上图所示,实现如下:
class oldMethod:
def C(self,m,n):
return math.factorial(m)//math.factorial(n)//math.factorial(m-n)
性能对比
我们这里从10到1000000从中选出20%的数,然后对3取余,其求解耗时情况如下,可以发现在m,n都比较大时,卢卡斯定理求解该问题性能可以用优异来形容。
-------当m=10,n=2时二者的求解耗时情况对比------
卢卡斯求解结果: 0
传统方法求解结果 0
卢卡斯方法耗时情况: 0.0 传统方法求值的耗时情况: 0.0
-------当m=100,n=20时二者的求解耗时情况对比------
卢卡斯求解结果: 0
传统方法求解结果 0
卢卡斯方法耗时情况: 0.0 传统方法求值的耗时情况: 0.0
-------当m=1000,n=200时二者的求解耗时情况对比------
卢卡斯求解结果: 0
传统方法求解结果 0
卢卡斯方法耗时情况: 0.0 传统方法求值的耗时情况: 0.0
-------当m=10000,n=2000时二者的求解耗时情况对比------
卢卡斯求解结果: 0
传统方法求解结果 0
卢卡斯方法耗时情况: 0.0 传统方法求值的耗时情况: 0.01999807357788086
-------当m=100000,n=20000时二者的求解耗时情况对比------
卢卡斯求解结果: 0
传统方法求解结果 0
卢卡斯方法耗时情况: 0.0 传统方法求值的耗时情况: 1.229926347732544
-------当m=1000000,n=200000时二者的求解耗时情况对比------
卢卡斯求解结果: 0
传统方法求解结果 0
卢卡斯方法耗时情况: 0.0 传统方法求值的耗时情况: 135.11490297317505
推导过程
网上有很多推导过程,个人觉得从二项式的性质方面的求解更容易理解,这里就不赘述,该天有了更精巧的推导方式再来补充。