部分参考刘扬俊,地址:https://blog.youkuaiyun.com/qq_19782019/article/details/85621386.
下面给出了从最简单的算指数mod1到快速幂算法的递进过程,前面的算法都有其劣势,到最后一种应付基本的大数幂时间上也不成问题了。
模运算:
1.幂模p : (a^b) % p = ((a % p)^b) % p=a% p*a^b-1%p;也就是说每乘一次a对p求一次模,循环b次
(a*b*c)%d=(a%d*b%d*c%d)%d=a%d*b%d*c%d; a%d和b%d成对的位置可以互换
如a^3%m=a%m*a%m*a%m
只是要相乘后再取模,就可以先取模再相乘,然后再取模这样算。
注意:(a % p)*(b % p)*(c % p)不可以直接去括号a % p*b % p*c % p 比如(35%3)*(243%3)!=35%3*245%3
2.模p加法:(a + b) % p = (a%p + b%p) % p
3.模p减法:(a - b) % p = (a%p - b%p) % p
4.模p乘法:(a * b) % p = (a % p*b % p) % p= ((a%p)*b)%p
//朴素模幂运算过程 最简单运算
/*这个算法的时间复杂度体现在for循环中,为O(n).这个算法存在着明显的问题,如果x和n过大,很容易就会溢出。
* java的普通数字类型是int,4个int的1024相乘(2^40)已经超出了int的表示范围了,2^32,
* 在乘方时Java发现结果(1000000*1000000)已经超出了int基本数据类型的最大范围(2147483647)输出负数,
* 原来java在计算时作了默认的类型提升(type promotion),中间结果做为long类型存放,返回结果时目标数据类型int不能
* 够容纳下结果,于是根据Java的基础类型的变窄转换(Narrowing primitive conversion)规则,把结果宽于int类型宽度的部 * 分全部丢弃,也就是只取结果的低32位,
* */
public static int mod1(int a,int b){//传入底数,指数和模
if(b==0){
return 1;
}else if(b==1){
return a;
}else{
int ans=1;
for(int i=0;i<b;i++){
ans*=a;
}
return ans;
}
}
// a^b%m的值 一般取模运算 公式 (a%m)^b%m 这种也一样会溢出 可能因为模数比较大 a%m就没效果
public static int mod2(int a,int b,int m){
if(b==0){
return 1;
}
else if(b==1){
return a%m;
}
else{
int ans=1;
for(int i=0;i<b;i++){
ans=ans*(a%m); //这种方法也可能会发生溢出
}
return ans%m;
}
}
//a^b%m =a%m*a^(b-1)%m 边乘边取模 最然很大的数可以求解但是时间很慢
public static int mod3(int a,int b,int m){
if(b==0){
return 1;
}else if(b==1){
return a%m;
}else{
int ans=1;
for(int i=1;i<=b;i++){
ans=ans*a%m; //防止乘的时候溢出 但是这样的算法复杂度还是太高
}
return ans;
}
}
//快速幂乘法 通过扩大底数 缩小指数 同时进行除模运算
public static long mod4(long a,long b,long m){
if(b==0){
return 1;
}else if(b==1){
return a%m;
}
long ans=1;
while(b>0){
if(b%2==0){ //如果指数为偶数
b/=2;
a=a*a%m;
}else{
b--;
ans=ans*a%m;
}
}
return ans;
}