• 学习记录:快速幂


    学习记录 快速幂

    快速幂的递归实现

    假设要算(7^9),如果采取普通计算,也就是(7*7*7*7*7*7*7*7*7),共需要8次运算。

    运用二分的思想,先算(7^4),然后通过(7^4*7^4*7)来计算$7^9 $,这样就只需要(3+1+1=6)次计算,然而这样还不够彻底,(7^4)还可以通过分解成(7^2*7^2)的形式,这样递归下去,就得到了时间复杂度为(O(logn))的快速幂算法。

    int Quickpow(int a,int n)
    {
    	if (n==0)
    		return 1;
    	else if (n%2==1)
    		return Quickpow(a,n-1)*a;
    	else{
    		int temp=Quickpow(a,n/2);//必须先保存下来,否则会算两遍
    		return temp*temp;
    	}
    }
    

    做题的时候,幂的结果可能会非常大,需要对一个大数取余,这时将上面的函数改成long long,在运算的每一步都要取余结果也要取余啊!改进代码如下:

    const int MOD=1e9+7;
    typedef long long ll;
    ll Quickpow(ll a,ll n)
    {
    	if (n==0)
    		return 1;
    	else if (n%2==1)
    		return Quickpow(a,n-1)*a%MOD;
    	else{
    		ll temp=Quickpow(a,n/2)%MOD;
    		return temp*temp%MOD;
    	}
    }//可以过洛谷P1226,就是结果也必须取余
    

    非递归实现

    非递归实现主要用了二进制的思想。

    在上面的递归实现中不难发现是每次都是将结果分割为两半,这正好对应了二进制。而且众所周知,位运算是比乘法运算快的,运用非递归实现因此比递归实现快一点。

    还是(7^9)的例子,(9)的二进制形式为(1001),意味着(7^{(1001)_2})可以拆分为(7^{(1000)_2}*7^{(1)_2}),以此类推,任何幂总可以拆分成(a^{2^b})相乘的形式,因此就有了思路。

    1. 总体思路就是从二进制最后一位开始算起,如果在这一位的二进制数为1,则说明需要加上这一块;若是0则跳过。然后到下一位。不过在此用位运算的右移实现。
    2. 计算(a^{2^b}),可以用底数自乘来实现,在每次运算中代表在这一位的(7^{2^{x}})是多少。
    3. 判断二进制的情况,需要用一些位运算的基本知识。

    非递归计算(7^9)的过程:

    临时变量 指数情况(二进制) 目前的运算结果
    7((7^{2^0 })) 1001 1*7=7
    49((7^{2^1})) 0100 7
    2401((7^{2^2})) 0010 7
    5764801((7^{2^3})) 0001 7*5764801=40353607

    代码实现:

    int Quickpow(int a,int n)
    {
    	int res=1;
    	while (n){			//最终右移的结果为0
    		if (n&1)		//指数二进制末尾为1
    			res*=a;		//乘以当前的底数
    		a*=a;			//底数自乘
    		n>>=1;			//指数右移一位
    	}
    	return res;
    }
    
  • 相关阅读:
    Ruby能否成为第二个Java
    攻防实践:一次入侵兼反入侵的实例
    微型PHP木马的探讨
    破解Session cookie的方法
    ASP马免杀工具—ASP万能溶剂
    微型PHP木马的探讨
    Ubuntu 8.04公布带给我们的商机
    Root logins are not allowed 的标题操持
    若何安装vixta之一若何哄骗Nero将vixta刻录成ISO光盘
    如何安排vixta之三Installing来源劈脸安排
  • 原文地址:https://www.cnblogs.com/Salty-Fish/p/12863502.html
Copyright © 2020-2023  润新知