快速幂

快速幂

一般而言,在计算\(a^n\)时,我们会使用暴力的方法,逐个去乘,这样操作时间复杂度是\(O(n)\)

而是用快速幂算法时间复杂度可以降到\(O(logn)\)

递归思想

在计算\(a^n\)时,我们可以先计算\(a^2\),然后在计算\((a^2)^2\),一直计算到\(n\)次幂。

例如\(3^9\),可以这样递归:
\(3^9=3*3^8\);
\(3^8=3^4*3^4\);
\(3^4=3^2*3^2\);
\(3^2=3^1*3^1\);
\(3^1=3^1*3^0\);
观察这个递归过程,我们可以发现:
\(n\)是偶数时,\(a^n=a^{n/2}*a^{n/2}\);
当n是奇数时,\(a^n=a*a^{n/2}\);
代码如下:

long long fastPow(long long a, long long n){
	if(n == 1) return a;
	long long ans = fastPow(a,n/2); 
	if(n % 2 == 1) return ans * ans * a; 
	else return ans * ans;
}

位运算

它是以数的二进制为基础,利用二进制的特性进行计算;

例如:计算\(2^{11}\),我们可以把它分成\(2^8\)\(2^2\)\(2^1\)的乘积,即\(2^{11}=2^8*2^2*2^1\)

那么现在可以思考三个问题:1、如何求\(2^8\)\(2^2\)\(2^1\)的值,2、如何把n分成\(11=8+2+1\),3、如何跳过不需要的部分。

1、如何求\(2^8\)\(2^2\)\(2^1\)的值
我们可以很容易发现\(2^1*2^1=2^2\)\(2^2*2^2=2^4\)\(2^4*2^4=2^8\)等等,都是2的倍数,并且都是倍乘关系,通过递推就可以实现。
2、如何把n分成\(11=8+2+1\)
用二进制我们便可以轻松理解,把n转换为二进制数,二进制数中的每一位的权值都是低一位的两倍,对应的\(a^i\)是倍乘关系,例如\(11_{10}=1011_2=2^3+2^1+2^0=8+2+1\)
3、如何跳过不需要的部分
例如\(2^{11}\),\(2^{11}=2^8*2^2*2^1\),需要跳过\(2^4\),在这里我们只需要做个判断即可,利用二进制的位运算很容易:
a、n&1,去n的最后一位,并且判断这一位是否需要跳过。
b、n>>=1,把n右移一位,去掉n的最后一位。

long long fastPow(long long a, long long n){
	long long ans = 1;
	while(n){
		if(n&1) ans *= a;
		a = a* a;
		n >>= 1;
	} 
	return ans;
}

现在我们在思考一个问题当\(n\)或者\(a\)比较大时,我们的结果往往都会非常大,如果直接计算的话就会发生溢出,所以我们在计算的过程中要对结果进行取模运算

typedef long long ll;
ll qpow(ll n, ll a, ll mod){
	ll res = 1;
	while(n){
		if(n & 1) res = ((res % mod) * (a % mod)) % mod;
		n >>= 1;
		a = ((a % mod) * (a % mod)) % mod;
	}
	return res % mod;
} 
posted @ 2021-02-13 20:02  h星宇  阅读(119)  评论(0编辑  收藏  举报