阅读目录
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
思路及Python实现
- 先读完题,就是要你模拟一个求一个数的次方,直接用内置函数 pow 哈哈哈
class Solution:
def Power(self, base, exponent):
return pow(base,exponent)
- 整体思路:利用乘法,不过得考虑底数是否为0,以及指数与0的关系。当指数exponent为0时,任何数的0次方都是1;当exponent不等于0,但底数base为0时,0的任何次方都为0;当底数不为0,这时候要区分指数exponent与0的关系:当指数大于0,直接将底数base连乘exponent次就好;当指数小于0,将指数取相反数,换成正数,然后在连乘,最后将结果取倒数。
class Solution:
def Power(self, base, exponent):
if exponent == 0:
return 1
if base == 0:
return 0
flag = True # 设置一个指数是否小于0的标志,默认为True,即指数为正数。
if exponent < 0:
exponent = -exponent
flag = False
result = 1
for i in range(exponent):
result *= base
return result if flag else 1 / result
递归做法
class Solution:
def Power(self, base, exponent):
if exponent == 0:
return 1
if base == 0:
return 0
flag = True # 设置一个指数是否小于0的标志,默认为True,即指数为正数。
if exponent < 0:
exponent = -exponent
flag = False
if exponent & 1 == 0: # 判断指数是否为偶数
res = self.Power(base, exponent // 2)
res *= res
else:
res = self.Power(base, exponent // 2)
res *= res
res *= base
return res if flag else 1 / res
快速幂
-
关于快速幂,举个例子:如要求 2 的10次方,之前的做法是:2连续乘以10次 时间复杂度为O(n),我们可以使用快速幂来优化:
-
关于a ^ b,还是以:2 ^10 为例:
对于b而言,如果我们将10变成二进制,就是:1010,如果变成加权的情况可以得到表达式:
0 * 2 ^ 0 + 1 * 2 ^ 1 + 0 * 2 ^ 2 + 1 * 2 ^ 3
代入原来的2^10可以得到表达式:
2 ^ (0 * 2 ^ 0 + 1 * 2 ^ 1 + 0 * 2 ^ 2 + 1 * 2^3),把为指数中为0的去掉,因为2 ^ 0是1,1再乘剩下的数还是本身,我们做下可以得到:
2 ^ (2 ^ 1) * 2 ^ (2^3) 仔细观察,指数中就是 二进制1010中 所有1 所对应的权位值
接下来就是如何确定二进制中的哪一位为1,这里可以利用位运算中的 & 和 >>(右移)运算。由于1的二进制除了第一位是1,其他的全是0,因此可以利用 n&1是否为0来判断n的二进制的当前最低位是否为1,如果n&1等于0,说明当前n的最低位不为1;利用右移运算来逐位读取。
所以指数的二进制数,不断右移,碰到0,就累乘;碰到1,就将累乘的值乘到结果中
上面那句话,可能不理解:那详细来说一下,首先为什么碰到0要将底数累乘?
因为,虽然你是位置是0,当前可能不要用到,但是随着右移,前面可能会出现 1 时,即是要求它当前位置的值,就是 2 ^ (位置-1)吧,那么你先帮我累乘,我就可以直接拿来用了,所以需要累乘!
def power(base,exponent):
res = 1
while exponent:
if exponent & 1: # 判断当前的最后一位是否为1,如果为1的话,就需要把之前的幂乘到结果中。
res *= base
base *= base # 一直累乘,如果最后一位不是1的话,就不用了把这个值乘到结果中,留着为后面服务
exponent = exponent >> 1 # 右移一位
return res
快速幂做法
class Solution:
def Power(self, base, exponent):
if exponent == 0:
return 1
if base == 0:
return 0
flag = False
if exponent < 0:
exponent = -exponent
flag = True
res = 1
while exponent:
if exponent & 1: # 判断当前的最后一位是否为1,如果为1的话,就需要把之前的幂乘到结果中。
res *= base
base *= base # 一直累乘,如果最后一位不是1的话,就不用了把这个值乘到结果中
exponent = exponent >> 1
return res if not flag else 1 / res
引申 快速幂取模
定理:(a * b) mod c = ((a mod c)*(b mod c)) mod c
那么根据上面的定理可以推导出另一个定理:
(a ^ b) mod c = (a mod c) ^ b mod c
(a ^ b) % c = (a%c) ^ (b %c)
将 (a%c) ^ (b %c) 化成整体来看,就是 A^B 所以,稍微转换下就能完成如下:
def power(base,exponent):
res = 1
base = a%c
while exponent:
if exponent & 1: # 判断当前的最后一位是否为1,如果为1的话,就需要把之前的幂乘到结果中。
res=(res*base)%c
base=(base*base)%c # 一直累乘,如果最后一位不是1的话,就不用了把这个值乘到结果中,留着为后面服务
exponent = exponent >> 1 # 右移一位
return res
比较下快慢
from time import *
def orginal_algorithm(a, b, c): # a^b%c
ans = 1
a = a % c # 预处理,防止出现a比c大的情况
for i in range(b):
ans = (ans * a) % c
return ans
def quick_algorithm(a, b, c):
a = a % c
ans = 1
# 这里我们不需要考虑b<0,因为分数没有取模运算
while b != 0:
if b & 1:
ans = (ans * a) % c
b >>= 1
a = (a * a) % c
return ans
time = clock()
a = eval(input("底数:"))
b = eval(input("指数:"))
c = eval(input("模:"))
print("朴素算法结果%d" % (orginal_algorithm(a, b, c)))
print("朴素算法耗时:%f" % (clock() - time))
time = clock()
print("快速幂算法结果%d" % (quick_algorithm(a, b, c)))
print("快速幂算法耗时:%f" % (clock() - time))