普通求幂运算
什么是快速幂?为什么要运用快速幂?快速幂又如何成为普遍算法的呢?
快速幂其实就是将一个数n的多次幂的求解,我们会这样做。
举个例子,计算2的200次方的最后面4位数(将结果%10000)
long long normalPower(long long base,long long power){
long long res=1;
for(int i=1;i<=power;i++){
res=res*base;
}
return result%1000;
}
可以带入运行一下,会发现这个结果为0???
噢~天呐,才算这么一点的数字,计算机就抗不了了!!
其实呢,我们要了解一下取模的运算法则
(a+b)%mod=(a%mod+b%mod)%mod;
(a-b)%mod=(a%mod-b%mod)%mod;
(a✖b)%mod=(a%mod✖b%mod)%mod;
没错我们会发现,可以每次求幂的过程中对两个数的运算结果取模实际等于对每个数进行取模运算后对结果进行取模。
因此呢,我们下一步就可以简化了
long long normalPower(long long base,long long power){
long long res=1;
for(int i=1;i<=power;i++){
res=res*base;
res=res%10000;
}
return res%10000;
然后我们再计算一下会发现结果为24!那么说明这个算法没有问题的,那么这样做的话,有100次幂,那么会进行100次的循环,那么200000000000000次方呢?来测试一下上面这种算法在oj的时间吧
#include <iostream>
#include <cmath>
#include <time.h>
using namespace std;
long long normalPower(long long base, long long power) {
long long result = 1;
for (int i = 1; i <= power; i++) {
result = result * base;
result = result % 1000;
}
return result % 1000;
}
int main() {
clock_t start, finish;
//clock_t为CPU时钟计时单元数
long long base, power;
cin >> base >> power;
start = clock();
//clock()函数返回此时CPU时钟计时单元数
cout << normalPower(base, power) << endl;
finish = clock();
//clock()函数返回此时CPU时钟计时单元数
cout << "the time cost is" << double(finish - start) / CLOCKS_PER_SEC;
//程序运行花费的CPU时钟单元数量等于inish与start的差值,再除每秒CPU的时钟单元,程序耗时就出来了
return 0;
我们会发现会将近用了18秒的时间呀!
快速幂算法的实现
在中学的时候,我们就会开始接触幂的概念,我们再复习一下幂的概念:
幂(power)是指数运算的结果。当m为正整数时,nm指该式意义为m个n相乘。当m为小数时,m可以写成a/b(其中a、b为整数),nm表示n^a再开b次根号。 [1] 当m为虚数时,则需要利用欧拉公式 eiθ =cosθ+isinθ,再利用对数性质求解。 [2] 把n^m看作乘方的结果,叫做n的m次幂,也叫n的m次方。
作为伟大的编程员,在这里底数我们用的base所表示,指数用的是power来表示,~ o( ̄▽ ̄)o
而快速幂的实现呢,可以从扩大底数base,减小指数power来缩小循环的时间
例如:2的100次方 就转换为 4的50次方
然后我们再看看,100次的循环就变成了50次的循环!在时间复杂度上就降低了一半!!
然后我们再进行一次转换, 4的50次方就变成了 16的25次方! 从100次的循环变成了25次的循环!
依次下去,会发现时间复杂度 从O(N)变成到O(logN)!
按照这个算法,快速幂的实现就成功了
偶数次幂:
power = power / 2;//把指数缩小为一半
base = base * base % 1000;//底数变大成原来的平方
奇数次幂:
power = power - 1;//把指数减去1,使其变成一个偶数
res = res * base % 1000;//此时记得要把指数为奇数时分离出来的底数的一次方收集好
power = power / 2;//此时指数为偶数,可以继续执行操作
base = base * base % 1000;
要注意奇数次幂的情况下,单独分离一个指数出来,记录这个结果,与偶数次的底数相加起来就得到结果了
上完整代码:
long long fastPower(long long base, long long power) {
long long res = 1;
while (power > 0) {
if (power % 2 == 0) {
//如果指数为偶数
power = power / 2;//把指数缩小为一半
base = base * base % 1000;//底数变大成原来的平方
} else {
//如果指数为奇数
power = power - 1;//把指数减去1,使其变成一个偶数
result = result * base % 1000;//此时记得要把指数为奇数时分离出来的底数的一次方收集好
power = power / 2;//此时指数为偶数,可以继续执行操作
base = base * base % 1000;
}
}
return result;
请注意结果返回的是res因为偶数次每次除以二,最后还是会变成1,也就会成为奇数次,结果呢就自然保存在res中了
计算一下时间,让人不可思议,竟然只花了0.002秒就求出了结果,然而普通的算法却用了将近18秒的时间才求出最后的结果。
是否对快速幂有了一种神奇的感受呢?那么赶紧来试一试吧!