快速幂算法,顾名思义,是一种快速求解幂值的方法,之前我们已经探讨过对时间复杂度的分析,通过之前的分析,我们了解了代码的运行速度对代码的优劣影响,我们在设计算法时,考虑时间复杂度至关重要。
快速幂算法可以简化幂的求值速度,将原本的暴力求法的时间复杂度O(n)降低为O(logn),大大降低了代码的运行速度。
这里我们以求2的1000000次方的值为例,很多人一看到这道题目,觉得并不是很难,一个for循环就解决了,也就是普通的暴力解法,暴力解法如下:
## 暴力求幂
A = 2
B = 1000000
result = 1
for i in range(B):
result = result*A
print(result)
很明显,当幂值特别大的时候,这个“暴力”求解需要消耗巨大的时间,时间复杂度O(n)
我们来举一个例子,例如 910
根据幂的运算法则,910 = (92)5 = (9*9)5
于是乎我们似乎发现一个可以降低时间复杂度的方法,因为原本需要乘10次的运算现在变成了只需要乘5次。
但是这个法则只适用于偶数幂,如果幂变成了奇数,如上面这个5的情况,该怎么办呢?
很显然,我们可以将奇数幂次减一变成偶数幂,把幂次为奇数的底数的一次方乘在右边,将分离出来的偶次幂继续重复以上的操作
即
910 = (92)5 = (92)4 * (92)1
(92)4 = (81)4 把幂次除以2,底数执行平方操作
= (812)2
(812)2 = (6561)2
(6561)2 = (43046721)1
此时,幂次又变成了一个奇数,为1,按照上面的方法,我们继续把幂次为奇数的底数的一次方,即(43046721)1拿出来,拿出来以后此时的指数已经变为了0,代表着对偶次幂的重复操作结束。于是我们可以观察到
910 = (92)5 = (92)4 * (92)1 = (812)2 * (92)1 = (6561)2 * (92)1 = (43046721)1 * (92)1
于是乎,快速幂算法提供了这样一个简便的解决思路:
当幂%2==0,也就是当幂为偶数时,根据幂的运算法则,我们可以将幂除以2,然后底数进行平方操作,值保持不变。
当幂%2= =1,也就是当幂为奇数时,将幂为奇数的底数保存起来,再对幂-1,重复上面的操作。
最后的结果就是将幂为奇数的底数综合相乘。
快速幂解法如下:
A = 2
B = 1000000
def fastpower(base,power):
result = 1
while(power>0):
if (power%2==0):
base = base*base
power/=2
else:
result = result*base
power-=1
power/=2
base = base*base
return result
fastpower(A,B)
print(fastpower(A,B))
时间复杂度O(logn)
在日常中,往往都不会叫你算这么大的一个数字,这么大的数字并不是计算机算不出,而是因为这个数字已经大到计算机(语言)已经装不下了。
所以在使用或者是比赛当中,往往会叫你求某个大数的个位数,后两位数,后三位数等等。
但是我们整体的结果都得不出,更不用说求这个数的某个部分了,所以我们要从前面运算的时候就开始着眼解决这个问题。
根据取余运算法则,我们知道
(a*b)%x = [(a%x) *(b%x)]%x
(a+b)%x = [(a%x) +(b%x)]%x
(a-b)%x = [(a%x) -(b%x)]%x
将上面的算法在每次运算的时候取余即可
代码如下:
A = 2
B = 1000000
def fastpower(base,power):
result = 1
while(power>0):
if (power%2==0):
base = base*base%1000
power/=2
else:
result = result*base%1000
power-=1
power/=2
base = base*base%=%1000
return result
fastpower(A,B)
print(fastpower(A,B))
快速幂算法可以将求幂解法的时间复杂度下降,将时间复杂度从O(n) 将为 O(logn),如果n的取值(幂的取值)足够大,时间消耗将大大减少。
同时,求余运算规则也要熟记,可以让我们无需得出总体结果的情况下得出总体结果的余数