zoukankan      html  css  js  c++  java
  • 快速乘法/幂 算法详解

    快速幂运算

    在计算 x时,我们会怎么算呢?如果只是x * x * x * ... * x 这样每个数乘起来计算 n 次的的话,虽然算法简单,但是复杂度为 O(n) ,往往不能满足要求。让我们来考虑加速幂运算的方法。

    思路一:

    如果 n = 2k ,可以将其表示为  xn = ((x2)2)... ,只要做 k 次平方运算就可以轻松求得。由此我们想到,先将 n 表示为2的幂次的和 n = 2k1 + 2k2 + 2k3 + ... ,就有 xn = x2^k1 x2^k2 x2^k3 ... ,只要在依次求 x2^i 的同时计算就好了,最终得到了 O(logn) 计算幂运算的算法。比如计算x22,可以把 x22 表示为 x* x4 * x16 (22转成二进制是10110)。在进行幂运算时,往往因为结果数字过大,而让我们输出取余后的结果。下面给出代码:

    typedef long long ll;
    
    ll mod_pow(ll x,ll n,ll mod){
        ll res = 1;
        while(n>0){
            if(n&1)  res = res * x % mod;  
          //如果二进制最低位为1,则乘上x^(2^i)
         x = x * x % mod;  //将x平方
         n >>= 1;
        }
        return res;
    }

    思路二:

    当n为偶数时有 xn = ((x2)(n/2)) ,递归转为n/2的情况;n为奇数时有 xn = ((x2)(n/2)) * x ,同样也递归转为 n/2 的情况。这样不断递归下去,每次n都减半,于是可以在O(logn)时间内完成幂运算。这个比第一种似乎更容易想到也更易理解,下面给出代码:

    typedef long long ll;
    
    ll mod_pow(ll x,ll n,ll mod){
        if(n == 0) return 1;
        if(n == 1) return  x % mod;
        ll res = mod_pow(x * x % mod, n / 2, mod);
        if(n & 1)
            res = res * x % mod;
        return res;
    }

    思路三:  

    还有一种非常类似的,f(x,n) = xn,x为奇数那么f(x,n) = f(x,n/2) * f(x,n/2) *x,x为偶数那么f(x,n) = f(x,n/2) * f(x,n/2)。

    ll f(int x,int n){
        if(n==0) return 1;
        if(n==1) return x;
        if(n&1) return f(x,n>>1)*f(x,n>>1)*x;  //如果n是奇数
        else return f(x,n>>1)*f(x,n>>1);   //如果n是偶数
    }
    

      

    快速乘法

    适用范围:快速计算a*b % mod的结果(主要目的是换乘法为加法,防止爆数据),或者快速计算a^b % mod 的结果,时间复杂度大大降低。

    算法描述:首先你可能会问a*b不是直接乘就出来了么,为什么需要快速算法?但是乘法在计算机中处理的时间并不是这么快的,也要拆分为加法来做的。所以快速乘法会更快的计算a*b的结果,而且a*b%mod可能还没取模就已经爆long long,但快速乘法却不会。快速幂也是同样的道理。

    实现的原理都是基于按照二进制位一步一步乘来避免重复的操作,利用前面的中间结果,从而实现快速的目的。

    对于乘数b来说,势必可以拆成2进制,比如110101。有一些位为0,有一些位为1。根据乘法分配律:a*b=a*(b1+b2+b3+……)
    那么对于a*53 = a*110101(二进制)= a*(100000+10000+100+1)=a*(100000*1+10000*1+1000*0+100*1+10*0+1*1)。
    那么设立一个ans=0用于保存答案,每一位让a*=2,在根据b的对应为1看是不是加上此时的a,即可完成快速运算。比如刚才的例子让a=5,运转流程如下。

    即可计算出5*53=265。

    不知道看到这里你发现了没有,其实对于快速幂其实是一样的道理,只不过每一位a更新的时候不是*2,而是a=a*a,ans+变成ans*。
    例如3^9的流程如下:3^5=3^(1001) (二进制)= 3^(1000*1+100*0+10*0+1*1)

    最后说一些细节吧,如果要取模在ans+、ans*、和a更新的时候都%mod即可。然后b一位一位的读取每次b/=2或者b>>=1,表示b向右移动一位,再与1做&操作即可。(见代码)

    #include <iostream> 
    #include <cstdio> 
    #include <algorithm>  
    #include <cmath>  
    #include <cstring>  
    #include <map>  
    using namespace std;
    
    long long q_mul(long long a,long long b,long long mod)
    // 快速计算 (a*b) % mod
    {
    	long long ans=0;
    	while(b)
    	{
    		if(b&1)	//如果当前位为1
    		{
    			b--;
    			ans =(ans+a)%mod;   //ans+=a
    		}
    		b>>=1;
    		a=(a+a)%mod;
    	}
    	return ans;
    }
    
    long long q_pow(long long a,long long b,long long mod)
    //快速计算 (a^b) % mod
    {
    	long long ans=1;
    	while(b)
    	{
    		if(b&1)
    			ans=q_mul(ans,a,mod); //ans*=a
    		b>>=1;
    		a=q_mul(a,a,mod);
    	}
    	return ans;
    }
    
    int main( )
    {
    	long long a, b, n;
    	while(cin >> a >> b >> n)
    	{
    		cout << "a*b%n = " << q_mul( a, b, n ) << endl;
    		cout << "a^b%n = " << q_pow( a, b, n ) << endl;
    	}
    	return 0;
    }
    
    // 本文部分参考:https://blog.csdn.net/maxichu/article/details/45459715
  • 相关阅读:
    Java数据结构——栈
    Java排序算法——插入排序
    Java排序算法——选择排序
    Java排序算法——冒泡排序
    Java查找算法——二分查找
    负载 压测 概念
    在线压力测试,测试网站并发量
    Jmeter如何把响应数据的结果保存到本地的一个文件
    性能测试之压力机瓶颈分析及优化
    系统吞吐量(TPS)、用户并发量、性能测试概念和公式
  • 原文地址:https://www.cnblogs.com/jaszzz/p/12692716.html
Copyright © 2011-2022 走看看