组合数取模Lucas定理及快速幂取模
组合数取模就是求的值,根据
,
和
的取值范围不同,采取的方法也不一样。
下面,我们来看常见的两种取值情况(m、n在64位整数型范围内)
(1) ,
此时较简单,在O(n2)可承受的情况下组合数的计算可以直接用杨辉三角递推,边做加法边取模。
(2) ,
,并且
是素数
本文针对该取值范围较大又不太大的情况(2)进行讨论。
这个问题可以使用Lucas定理,定理描述:
其中
这样将组合数的求解分解为小问题的乘积,下面考虑计算C(ni, mi) %p.
已知C(n, m) mod p = n!/(m!(n - m)!) mod p。当我们要求(a/b)mod p的值,且a很大,无法直接求得a/b的值时,我们可以转而使用乘法逆元k,将a乘上k再模p,即(a*k) mod p。 其结果与(a/b) mod p等价。
那么逆元是什么?
定义:满足a*k≡1 (mod p)的k值就是a关于p的乘法逆元(当p是1时,对于任意a,k都为1)
除法取模,这里要用到m!(n-m)!的逆元。
根据费马小定理:
已知gcd(a, p) = 1,则 ap-1 ≡ 1 (mod p), 所以 a*ap-2 ≡ 1 (mod p)。
也就是 (m!(n-m)!)的逆元为 (m!(n-m)!)p-2 ;
下面附上Lucas定理的一种证明,见下图,参考冯志刚《初等数论》第37页。
题意:求,其中
,并且
是素数。
代码:
#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 = ;
int base = a;
while(b>){
if(b & ==){
result = (result*base) % m;
}
base = (base*base) %m;
b>>=;
}
return result;
}
//计算组合数取模
ll comp(ll a, ll b, int p) {//composite num C(a,b)%p
if(a < b) return ;
if(a == b) return ;
if(b > a - b) b = a - b; int ans = , ca = , cb = ;
for(ll i = ; i < b; ++i) {
ca = (ca * (a - i))%p;
cb = (cb * (b - i))%p;
}
ans = (ca*quick_power_mod(cb, p - , p)) % p;
return ans;
}
ll lucas(ll n, ll m, ll p) {
ll ans = ; 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,)<<endl;
}
return ;
}
上面的代码中用到了求幂取模操作来计算(m!(n-m)!)p-2 % p.下面解释幂取模算法:
反复平方法 求ab%m
通过研究指数b的二进制表示发现,对任意的整数b都可表示为:
- n表示b的实际二进制位数
- bi表示该位是0或1
因此,ab可表示为:
即用b的每一位表示a的每一项,而对任意相邻的两项存在平方关系,即:
因此我们构造下面的算法:
- 把b转换为二进制表示,并从右至左扫描其每一位(从低到高)
- 当扫描到第i位时,根据同余性质(2)计算a的第i项的模:
base变量表示第i-1位时计算出的模,通过递归能很容易地确定所有位的模。 - 如果第i位为1,即bi=1,则表示该位需要参与模运算,计算结果 result = (result*base) mod m;其中result为前i-1次的计算结果;若bi=0,则表示a的第i项为1,不必参与模运算
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;
}
其中运用了两个同余性质:
同余性质1:ab≡bc (mod m)
同余性质2: a≡c (mod m) => a2≡c2 (mod m)
理解要点:
- base记录了a的每项的模,无论b在该位是0还是1,该结果都记录,目的是给后续位为1的项使用,计算方式是前一结果的平方取模,这也是反复平方法的由来
- result只记录了位为1的项的模结果,该计算方式使用了同余性质1
- 通过地把a使用二进制表示,并结合同余性质1,2,巧妙地化解了大数取模的运算。对1024位这样的大数,也最多进行1024次循环便可计算模值,性能非常快。
该方法是许多西方数学家努力的结果,通常也称为Montgomery算法。
(以上部分内容由网络搜集整理而来,不当之处,烦请不吝赐教)
组合数取模Lucas定理及快速幂取模的更多相关文章
- HDU 1061 Rightmost Digit --- 快速幂取模
HDU 1061 题目大意:给定数字n(1<=n<=1,000,000,000),求n^n%10的结果 解题思路:首先n可以很大,直接累积n^n再求模肯定是不可取的, 因为会超出数据范围, ...
- 数学--数论--HDU 4675 GCD of Sequence(莫比乌斯反演+卢卡斯定理求组合数+乘法逆元+快速幂取模)
先放知识点: 莫比乌斯反演 卢卡斯定理求组合数 乘法逆元 快速幂取模 GCD of Sequence Alice is playing a game with Bob. Alice shows N i ...
- HDU-2817,同余定理+快速幂取模,水过~
A sequence of numbers Time Limit: 2000/1 ...
- POJ 1845-Sumdiv(快速幂取模+整数唯一分解定理+约数和公式+同余模公式)
Sumdiv Time Limit:1000MS Memory Limit:30000KB 64bit IO Format:%I64d & %I64u Submit Statu ...
- 【转】C语言快速幂取模算法小结
(转自:http://www.jb51.net/article/54947.htm) 本文实例汇总了C语言实现的快速幂取模算法,是比较常见的算法.分享给大家供大家参考之用.具体如下: 首先,所谓的快速 ...
- UVa 11582 (快速幂取模) Colossal Fibonacci Numbers!
题意: 斐波那契数列f(0) = 0, f(1) = 1, f(n+2) = f(n+1) + f(n) (n ≥ 0) 输入a.b.n,求f(ab)%n 分析: 构造一个新数列F(i) = f(i) ...
- POJ3641-Pseudoprime numbers(快速幂取模)
题目大意 判断一个数是否是伪素数 题解 赤果果的快速幂取模.... 代码: #include<iostream> #include<cmath> using namespace ...
- 九度OJ 1085 求root(N, k) -- 二分求幂及快速幂取模
题目地址:http://ac.jobdu.com/problem.php?pid=1085 题目描述: N<k时,root(N,k) = N,否则,root(N,k) = root(N',k). ...
- HDU--杭电--4506--小明系列故事——师兄帮帮忙--快速幂取模
小明系列故事——师兄帮帮忙 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) To ...
随机推荐
- usb驱动开发18之设备生命线
现在已经使用GET_DESCRIPTOR请求取到了包含一个配置里所有相关描述符内容的一堆数据,这些数据是raw的,即原始的,所有数据不管是配置描述符.接口描述符还是端点描述符都挤在一起,所以得想办法将 ...
- Spring Security笔记:HTTP Basic 认证
在第一节 Spring Security笔记:Hello World 的基础上,只要把Spring-Security.xml里改一个位置 <http auto-config="true ...
- Silverlight:针式打印机文字模糊的改善办法
SL的打印功能,如果使用针式打印机,打出来的字很模糊,网上有一些文章介绍应该使用"Arial,SimSun"(即:宋体),但实际测试的结果,宋体依然很模糊. 下面是各种字体的测试: ...
- caffe的python接口学习(2):生成solver文件
caffe在训练的时候,需要一些参数设置,我们一般将这些参数设置在一个叫solver.prototxt的文件里面,如下: base_lr: 0.001 display: 782 gamma: 0.1 ...
- Oracle 常用函数
主要是对项目中用过的 oracle 函数进行总结,并做出目录,方便后续项目是快速查找,提高效率. 01.Round (数值的四舍五入) 描述:传回一个数值,该数值是按照指定的小数位元数进行四舍五入运算 ...
- SQL基础之GROUPING
1.grouping sets 记得前几天第一次接触grouping sets时,笔者的感觉是一脸懵逼. 后来一不小心看到msdn上对grouping sets的说明,顿时豁然开朗,其实groupin ...
- 用H5+Boostrap做简单的音乐播放器
前言:这个是综合一下我最近在学的东西做的小Demo,到实际使用还有距离,但是用来练手巩固知识点还是不错的,最近在二刷JS书和Boostrap.css的源码,做完这个Demo也算是暂告一段落,接下来是j ...
- Java学习笔记(二二)——Java HashMap
[前面的话] 早上起来好瞌睡哈,最近要注意一样作息状态. HashMap好好学习一下. [定义] Hashmap:是一个散列表,它存储的内容是键值对(key——value)映射.允许nul ...
- 12-rm 命令总结
rm remove files or directories 删除目录或文件 [语法]: rm [选项] [参数] [功能介绍] rm命令可以删除一个目录中的一个或多个文件或目录,也可以将某个目录及其 ...
- 数据库高可用架构(MySQL、Oracle、MongoDB、Redis)
一.MySQL MySQL小型高可用架构 方案:MySQL双主.主从 + Keepalived主从自动切换 服务器资源:两台PC Server 优点:架构简单,节省资源 缺点:无法线性扩展,主从失 ...