面試題 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競賽中題中涉及到過此類賽題,且有結果取模問題,一篇很有價值的文章。
快速乘法:類似於上面的快速乘法,只是把乘號改成加號。
舉例 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