[位运算] 64位整数乘法(mod 一个数)

64位整数乘法

  在部分题目中可能涉及到长整数(long long)的相关计算,可当我们需要将两个长整数相乘时有可能出现溢出的情况,这时候通常需要进行高精度计算,可如果我们需要将两个长整数相乘并 mod 一个数时高精度计算也就不太方便了,这时我们需要一个优秀的算法让我们快速的计算出类似 a×b mod c 的数值

[算法描述]

  我们用快速幂的思想,将b用二进制表示

b=ck-12k-1+ck-22k-2+…+c020

  那么:

 a×b=a×(ck-12k-1+ck-22k-2+…+c020)=a×ck-1×2k-1+a×ck-2×2k-2+…+a×c0×20

  又

a×2i=(a×2i-1)×2

  于是我们可以递推地计算出a×2i并且每次计算把答案加入ans中与快速幂不同的是ans的初始化并不是赋值为1而是清0.时间复杂度为O(log2 b).

  在此基础上我们考虑如果a,b是负数那怎么办呢?为了解决这个问题我们考虑打一个标记w,我们将w的初始值赋值为1,如果a,b的值是负数的话就把w值取w=-w(a,b均做一次判断),我们把返回ans×w即可.

  如果b的值远大于a时O(log2 b)这个时间复杂度相对于O(log2 a)而言就不是那么优秀了,于是我们可以进一步优化,如果a比b的数值小的话就交换a和b的数值,那么这个时间复杂度最终为O(log2 min(a,b))

[代码]

/*
    Name: Multiplication 
    Author: FZSZ-LinHua
    Date: 2018 06 07
    Time complexity: O(log min(a,b))
    Algorithm: Binary calculation 
*/
# include "iostream"
# include "cstdio"

using namespace std;

long long mul(long long x,long long y,long long z){    //计算x*y mod z的数值 
    long long ans=0;
    int w=1;
    if(y<0){    //如果y<0那么标记一下,y取绝对值 
        w=-w;
        y=-y; 
    }
    if(x<0){    //如果x<0那么标记一下,x取绝对值 
        w=-w;
        x=-x; 
    }
    if(x<y){    //如果x比y小那么交换x,y 
        swap(x,y); 
    } 
    while(y){
        if(y&1){    //如果y的i位不为0就加入答案中 
            ans=(ans+x)%z; 
        }
        x=(x*2)%z;    //递推计算a^2i 
        y>>=1;     //舍弃最低位 
    }
    return w*ans;//返回答案 
}

int main(){
    long long a,b,c; 
    scanf("%lld%lld%lld",&a,&b,&c);
    printf("%lld",mul(a,b,c));
    return 0; 
} 

猜你喜欢

转载自www.cnblogs.com/FJ-LinHua/p/9152631.html