例题:nyoj 102 快速求幂
1 //a^b mod c=(a mod c)^b mod c//很容易设计出一个基于二分的递归算法。
2 #include<stdio.h>
3 #include<stdlib.h>
4 //快速幂算法,数论二分
5 long long powermod(int a,int b, int c) //不用longlong就报错,题目中那个取值范围不就在2的31次方内
6 {
7 long long t;
8 if(b==0) return 1%c;
9 if(b==1) return a%c;
10 t=powermod(a,b/2,c);//递归调用,采用二分递归算法,,注意这里n/2会带来奇偶性问题
11 t=t*t%c;//二分,乘上另一半再求模
12 if(b&1) t=t*a%c;//n是奇数,因为n/2还少乘了一次a
13 return t;
14 }
15 int main()
16 {
17 int n;
18 long long a,b,c;
19 scanf("%d",&n);
20 while(n--)
21 {
22 scanf("%lld%lld%lld",&a,&b,&c);
23 printf("%lld\n",powermod(a,b,c));
24 }
25 return 0;
26}
刚开始用pow函数求次幂,后来发现结果以及参数都为浮点型,我就强行转化为(int)得出的结果不对,看来这种方法不行
一旦遇到高次方连乘,一定要记得快速幂。
2:快速幂取模
快速幂取模就是在O(logn)内求出a^n mod b的值。算法的原理是ab mod c=(a mod c)(b mod c)mod c
因此很容易设计出一个基于二分的递归算法。
心得:long long 2^63 大概是 9*10^18,假如999999999*999999999*999999999肯定超过int 64位的长度了,所以一定溢出。
以下是我的代码,以下代码必须保证输入的是合法的表达式,比如不能出现0^0 mod b:
long exp_mod(long a,long n,long b)
{
long t;
if(n==0) return 1%b;
if(n==1) return a%b;
t=exp_mod(a,n/2,b);
t=t*t%b;
if((n&1)==1) t=t*a%b;
return t;
}
3:高次方求模:
比如a的b次方对c求模
我们可以把b 化为二进制形式看那一位有1
比如b=10101则 a^b=a^(10000)*a^(100)*a^(1)
以函数形式体现:
long long a,b,c;
void han()
{
long long t,s;
for(t=a,s=1;b;b>>=1,t*=t,t%=c)//用b>>=1查看b中1
if(b&1){s*=t;s%=c;}
printf("%lld\n",s%c);
}
4:据说,矩阵快速幂在递推式优化上相当神奇,而且效率很高。。。
两矩阵相乘,朴素算法的复杂度是O(N^3)。如果求一次矩阵的M次幂,按朴素的写法就是O(N^3*M)。既然是求幂,不免想到快速幂取模的算法,这里有快速幂取模的介绍,a^b %m 的复杂度可以降到O(logb)。如果矩阵相乘是不是也可以实现O(N^3 * logM)的时间复杂度呢?答案是肯定的。
5:同余幂的思想
求出同余幂bn mod m,其中b,n,m都是比较大的整数。例如取b=12345678,n=456789,直接计算显然是不可行的,可以把n按二进制展开,则n=456789就变成了1101111100001010101,这样每次只需要求b mod m,b2 mod m,... b2^(k-1) mod m,然后把对应位置上的二进制是1的项乘起来,每次乘完后求除m的余数即可,大大降低了计算的复杂度。
这里还要用一些同余定理
(a+b)mod m=((a mod m)+(b mod m))mod m;
a*b mod m=(a mod m)*(b mod m) mod m;
a^b mod m=(a mod m)^b mod m;