面试题 1+2+...+n 不用for循环等,题解中方法快速幂和快速乘法的知识盲点补充内容。
快速幂:就是快速算底数的n次幂。其时间复杂度为 O(log₂N), 与朴素的O(N)相比效率有了极大的提高。
求a的b次方幂。(a=3,b=11)
故做法:将b转成二进制,用i去循环b中的每一位,若该位置为1,那么就乘以。直至b为0。
def exponential_calculation(a,b): #迭代版本
ans = 1
while(b):
if( b&1 ) : #如果b的当前末位为1,则相乘
ans *= a #ans乘上当前的a,决定是否*(a^2);*(a^4);*(a^8)
a *= a #a自乘,构造 a^2 a^4 a^8 a^16..
b >>= 1 #b往右移一位,判断每个位置
return ans
def t(a,b): ##递归版本
if not b :return 1 #while结束条件
if b&1 : # while 里面的if判断
return a*t(a*a,b>>1)
else:
return 1*t(a*a,b>>1)
思考:【1】为什么b&1返回的就是b的最后一位是0还是1?(利用b&1来判断b的奇偶性,b的最后一位是1 奇数反之偶数)
不是与1做按位与运算,而是与 1 = 0b0001做按位与运算。
假设 b(5) = 0b0101 & 0b0001(1)
可以看出 除了末位之外的位与0做按位与肯定是0,所以只看最后一位是什么,如果是1则1,是0则0。那么每次就可以查看b的最后一位,并且可以利用b&1来判断b的奇偶性,b的最后一位是1 就是 奇数反之偶数。
【2】ACM竞赛中题中涉及到过此类赛题,且有结果取模问题,一篇很有价值的文章。
除了上面的这种公式的思想外,还有另外一种思考方式[二分法角度]。下面的二分法角度思想摘自:此链接。
当 n 为偶数的时候, "//"整除的意思 向下取整
当 n 为奇数的时候,
def exponential_calculation(a,b): #迭代版本
ans = 1
while(b):
if( b & 1 ) : #判断奇偶性,而不是判断最后一位是0还是1
ans *= a #奇数 多乘以一个 a
a *= a #a自乘,构造 a^2 a^4 a^8 a^16..
b >>= 1 #b往右移一位 b/2 b/4 b/8 b/16 直到b为0
return ans
快速乘法:类似于上面的快速乘法,只是把乘号改成加号。
举例 a*b (3*10) ----->>
def t(a,b): #迭代
ans = 0
while(b):
if b&1:
ans += a # 实现对应位置相加
a += a ## 实现 a*2 a*4 a*8
# a <<= 1 效果同上
b >>= 1
return ans
def t(a,b): ##递归版本
if not b : return 0 #while结束条件
if b&1 : # while 里面的if判断
return a+t(a+a,b>>1)
# return a+t(a<<1,b>>1)
else:
return 0+t(a+a,b>>1)
在上面的问题中,没有考虑溢出的问题。有人说python不用考虑溢出,又有人说python的确会有溢出问题。[我现在也没发现特别好的文章来说这个点,如果有再补充把]
在c++等程序语言中,通常利用求余的方法来解决溢出问题,上面的ACM竞赛题也是。上面文章链接有分析就不写了。
参考:
【1】https://zhuanlan.zhihu.com/p/95902286
【2】https://blog.csdn.net/Harington/article/details/87602682