64位整数乘法
在部分题目中可能涉及到长整数(long long)的相关计算,可当我们需要将两个长整数相乘时有可能出现溢出的情况,这时候通常需要进行高精度计算,可如果我们需要将两个长整数相乘并 mod 一个数时高精度计算也就不太方便了,这时我们需要一个优秀的算法让我们快速的计算出类似 a×b mod c 的数值
[算法描述(1)]
我们用快速幂的思想,将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))当然如果a,b均大于c时可以实现对a,b mod c进一步优化时间复杂度
[代码(1)]
/* 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; }
[算法描述(2)]
对于上述算法我们思考能不能用更短的时间内算出a×b mod c的数值呢?我们发现一个式子
a×b mod c=a×b-[a×b/c]×c([x]表示x下取整)
我们可以先处理除[a×b/c]×c的数值,算ans时将a×b减去该数值即可,注意计算出的ans值可能为负数也可能大于c所以我们需要对ans加上c再mod c及(ans+c) mod c
[代码(2)]
/* Name: Multiplication Author: FZSZ-LinHua Date: 2018 06 07 Time complexity: O(1) Algorithm: Brute-force */ # 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 a=(long double) x*y/z; long long ans=x*y-a*z; ans=(ans+z)%z; return ans; } int main(){ long long a,b,c; scanf("%lld%lld%lld",&a,&b,&c); printf("%lld",mul(a,b,c)); return 0; }
原文地址:https://www.cnblogs.com/FJ-LinHua/p/9152631.html