六十八、快速冪算法、牛頓迭代法、累加數組+二分查找的變形

2021-03-02 Python之王

「@Author:Runsen」

編程的本質來源於算法,而算法的本質來源於數學,編程只不過將數學題進行代碼化。「---- Runsen」

上次介紹了二分查找算法及其四個變形問題,下面介紹二分法常用的場景和典型的例題。

快速冪算法

題目是來自Leetcode:50. Pow(x, n),https://leetcode-cn.com/problems/powx-n/

實現 pow(x, n) ,即計算 x 的 n 次冪函數。

示例 1:

輸入: 2.00000, 10
輸出: 1024.00000

其實快速冪相關的問題,個人覺得只要你是一名程式設計師,就必須要掌握快速冪算法。

當我們遇到一個問題需要讓我們求得一個數 n 的 m 次方,那麼最簡單的方法是將 n 乘以 m 次,得到結果,但是如果我們現在需要計算的是 2^10000000 這樣的式子呢,顯然如果我們的程序需要計算 2 的更高次方的時候這樣的算法,對於算法競賽而言時間上顯然是不允許的,因此提出了快速冪算法。

其實在計算這樣的式子的時候有大量的運算步驟是可以避免的,我們現在拿

按照上面的計算式,計算這樣的式子,我們需要進行 8 次計算。

那麼如果我們換一種思路來計算呢:

快速冪實際上是分治思想的一種應用。因此,可以得到:

下面就需要判斷n/2的奇偶的情況。

觀察發現,當 n 為奇數時,二分後會多出一項 x 。比如:。

當n為奇數,

當n為偶數,

根據推導,每次把冪從 n 降至 n//2 ,直至將冪降為 0 。

如果x = 0時:直接返回0,初始化 res = 1,

n = 5
x = 3
res = 1
while n:
  if n % 2 == 1:
    res *= x
  x *= x
  n //= 2
print(res)

243

我們可以使用位運算寫成比較高級的代碼。

快速冪算法如果x等於0,直接返回0。如果小於0 ,x需要取其倒數,n取其相反數。

class Solution:
    def myPow(self, x: float, n: int) -> float:
        if x == 0.0 : return 0.0
        res = 1
        if n < 0: x, n = 1 / x, -n
        while n:
            if n & 1: res *= x
            x *= x
            n >>= 1
        return res

求x的平方根

題目是來自LeetCode 第 69題:x 的平方根,https://leetcode-cn.com/problems/sqrtx/

計算並返回 x 的平方根,其中 x 是非負整數。

# 由於返回類型是整數,結果只保留整數的部分,小數部分將被捨去。 
# 示例 1: 
# 輸入: 4
# 輸出: 2
# 示例 2: 
# 輸入: 8
#輸出: 2
#說明: 8 的平方根是 2.82842..., 
#     由於返回類型是整數,小數部分將被捨去。

二分查找的下界為 0

這個題目的話,使用二分查找,low是0,high是數字 x/2 + 1。我們需要查找一個數mid的平方小於等於x。因此,可以進行二分查找。需要注意的是返回的是high。

class Solution:
    def mySqrt(self, x: int) -> int:
        low = 0 
        high = int(x/2 + 1)
        while low <= high :
            mid = (low + high) // 2
            if mid ** 2 == x :
                return mid
            elif mid ** 2 > x :
                high = mid - 1
            elif mid ** 2 < x :
                low = mid + 1
        return high

還有一種方法是牛頓法, 利用一階線性近似快速尋找最優點。

牛頓迭代法是用切線來估計曲線,我們可以在某個點上做切線得到切線方程y=f』(x0)(x-x0)+f(x0),然後找出切線與橫軸的交點,就是下一個迭代點。

迭代點和零點具體一個的關係:

牛頓法的公式具體推導:

最終得到:

比如求3的平方根,就是求曲線f(x)=x^2-3的零點,求導f』(x)=2*x,所以我們首先取x0=2,則x1 = 2 – 1 /4 = 1.75,然後x2 = 1.75 – [(1.75)^2 – 3]/(2*1.75) = 1.73214… 這個數就跟根號3很接近了。

class Solution:
    def mySqrt(self, x: int) -> int:
        if x < 2:
            return x
        x0 = x
        x1 = x0/2 + x/(x0*2) 
        while abs(x0 - x1) >= 1:
            x0 = x1
            x1 = x1 / 2 + x / (x1 * 2)
        return int(x1)

有的時候,需要對平方根的誤差進行估算,需要是小數點後6位,牛頓法瞬間解決。

def mySqrt(x: int) -> int:
    if x < 2:
        return x
    x0 = x
    x1 = x0 / 2 + x / (x0 * 2)
    while abs(x0 - x1) >= 0.000001:
        x0 = x1
        x1 = x1 / 2 + x / (x1 * 2)
    return x1

print(mySqrt(3))
# 1.7320508075688772

寫作業(累加數組+二分查找的變形)

阿里雲天池賽在線編程:在線編程限時賽。賽題連結:https://tianchi.aliyun.com/oj/9087601630820991/54270154473935464。此題為中等難度題目。

描述:n個人,他們每個人需要獨立做m份作業。第i份作業需要花費cost[i]的時間。由於每個人的空閒時間不同,第i個人有val[i]的時間,這代表他做作業的總時間不會超過val[i]。每個人都按照順序,從1號作業開始,然後做2號,3號. 現在,你需要計算出他們最多花了多少的時間。

1<=n<=100000 1<=m<=100000 1<=val[i]<=100000 1<=cost[i]<=100000

示例
樣例 1:

給定`cost=[1,2,3,5]`,`val=[6,10,4]`,返回`15`。
輸入:
[1,2,3,5]
[6,10,4]
輸出
15

解釋:
第一個人可以完成1號作業,2號作業,3號作業,1+2+3<=6。
第二個人可以完成1號作業,2號作業,3號作業,無法完成4號作業,1+2+3<=10,1+2+3+5>10。
第三個人可以完成1號作業,2號作業,無法完成3號作業,1+2<=4,1+2+3>4。
1+2+3+1+2+3+1+2=15,返回15。

樣例 2:


給定 `cost=[3,7,3,2,5]`,`val=[10,20,12,8,17,25]`,返回`78`.
輸入:
[3,7,3,2,5]
[10,20,12,8,17,25]
輸出:
78

解釋:
第一個人可以完成1號作業,2號作業, 3 + 7<=10.
第二個人可以完成1號作業,2號作業,3號作業,4號作業和5號作業, 3+7+3+2+5<=20
第三個人可以完成1號作業,2號作業,無法完成三號作業,  3+7<=12,3+7+3>12.
第四個人可以完成1號作業,無法完成2號作業 , 3<=8,7+3>8.
第五個人可以完成1號作業,2號作業,3號作業,4號作業,無法完成5號作業,3+7+3+2<=17,3+7+3+2+5>17.
第六個人可以完成1號作業,2號作業,3號作業,4號作業和5號作業, 3+7+3+2+5<=25
3+7+3+7+3+2+5+3+7+3+3+7+3+2+3+7+3+2+5=78, 返回 78.

「這個題其實就是二分查找的變形,查找排序數組中,第一個大於目標值的前一個值或者等於目標值(數組中可能不存在目標值)」。這個排序數組就是cost的累加數組。

class Solution:
    """
    @param cost: the cost
    @param val: the val
    @return: the all cost
    """
    def doingHomework(self, cost, val):
        # Write your code here.
        sum = 0
        sums = []
        for i in cost:
            sum += i
            sums.append(sum)
        result = 0
        for n in val:
            if n != -1:
                result += binary_search(sums, n)
        return result

def binary_search(list, num):
    if num >= list[-1]:
        return list[-1]
    if num < list[0]:
        return 0
    left = 0
    right = len(list) - 1  # 注意1 low和high用於跟蹤在其中查找的列表部分
    while left < right:
        mid = left + (right-left) // 2
        if list[mid] <= num and list[mid +1 ] > num:
            return list[mid]
        elif list[mid] > num  and list[mid-1] <= num:
            return list[mid -1 ]
        elif list[mid] < num:
            left = mid + 1
        else:
            right = mid -1
    return 0

「人生最重要的不是所站的位置,而是內心所朝的方向。只要我在每篇博文中寫得自己體會,修煉身心;在每天的不斷重複學習中,耐住寂寞,練就真功,不畏艱難,奮勇前行,不忘初心,砥礪前行,人生定會有所收穫,不留遺憾 (作者:Runsen )」

本文已收錄 GitHub,傳送門~[1] ,裡面更有大廠面試完整考點,歡迎 Star。



Reference[1]

傳送門~: https://github.com/MaoliRUNsen/runsenlearnpy100

相關焦點

  • 二分查找算法及其應用
    二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。
  • 二分查找算法案列詳解
    1、前言最近刷了很多二分查找相關的題目,這裡將近期的收穫做一個總結,包括二分查找的變形問題。如果能掌握,我相信以後基本上二分查找相關的問題對你來說,都不是問題。2、二分查找的效率二分查找是啥我想不用過多的說明。
  • 二分查找和插值查找
    如果程序的查找算法設計的很精妙,那麼整個程序的性能就會有顯著的提升。今天就來分享兩種常用的查找算法。關於查找最容易想到的一種實現方式就是將一組數據中的每個元素逐個取出來和目標值比較,如果取出來的元素和目標值相同那就說明找到了,這種查找算法叫做順序查找法,其複雜度為O(N)。
  • 二分查找和大O表示法
    代碼實現:       編寫執行二分查找的python代碼,代碼示例使用數組(在python中叫做列表)        tips:1、給出的序列必須有序,才可以進行二分查找                 2、在python3中,/表示浮點數除法,//表示整數除法
  • 算法入門——二分查找法
    二分查找法      假設我們要在一個電話簿中查找一個名字為g名字開頭的人,我們可以從頭開始翻頁
  • 二分查找的妙用:判定子序列
    二分查找本身不難理解,難在巧妙地運用二分查找技巧。對於一個問題,你可能都很難想到它跟二分查找有關,比如前文 最長遞增子序列就藉助一個紙牌遊戲衍生出二分查找解法。今天再講一道巧用二分查找的算法問題:如何判定字符串s是否是字符串t的子序列(可以假定s長度比較小,且t的長度非常大)。
  • Leetcode刷題-二分查找
    本文對部分涉及二分查找算法的leetcode題目進行了學習與實踐,並給出了個人的二分查找python模板二分查找算法解釋二分查找算法(英語:binary search algorithm),也稱折半搜索算法(英語:half-interval search algorithm
  • NOIP複賽知識點簡述及複賽算法總結!
    3、簡單操作:如篩法、前綴和、快速冪、高精度、輾轉相除法等,掌握全面即可應對大部分處理數據上的問題。 4、隊列(單調隊列)、棧、堆、鍊表等基礎數據結構。 5、簡單二分和分治(快速排序,歸併排序)。 6、貪心,要保證貪心的正確性,如果無法證明也可以用來騙分。
  • 算法-數組中是否存在兩數之和等於x?
    這是算法導論中的一道題目,主要是運行時間為nlogn,如果沒有運行時間限制,可以暴力運行n^2就可以得出結論。在這裡給出的解決方案運用到了分治法和二分查找法。代碼1.解題思路是先給數組排序,用分治法,運行時間為nlogn。
  • 算法基礎知識 目錄
    >算法必備基礎講解具體數據結構前的必備基礎知識,再次探討時間/空間複雜度,了解一些通用的基本算法:遞歸算法,分治法。算法中的迭代3-6 分治法分治策略,分治法三步驟,合併排序3-7 特殊的時間和空間複雜度二分查找時間複雜度,數學題中的冪,開放等特殊複雜度第四章 數組數組這個結構很多人不是非常在意,但在面試之中,一旦考察基礎知識
  • 圖解:三款經典的查找算法,等著你
    轉自景禹今天我們一起來看三款經典的排序算法!跳躍查找(Jump Search)與二分查找類似,跳躍查找也是在有序數組上進行的。基本思想是以固定步長向前跳越,從而跳過儘可能多的不在待查找元素附近的元素,減少查找過程中的比較次數。
  • 七大查找算法
    本文簡單概括性的介紹了常見的七種查找算法,說是七種,其實二分查找、插值查找以及斐波那契查找都可以歸為一類——插值查找。插值查找和斐波那契查找是在二分查找的基礎上的優化查找算法。樹表查找和哈希查找會在後續的博文中進行詳細介紹。  查找定義:根據給定的某個值,在查找表中確定一個其關鍵字等於給定值的數據元素(或記錄)。
  • 聊聊一看就會一寫就跪的二分查找
    要說哪個算法的知名度較高,二分查找一定排得上號。以至於在面試的時候,如果應聘者簡歷寫了擅長算法或者參加過ACM競賽之類,我都會讓他現場寫一道二分查找的題目:// 已知array數組的元素為單調非遞減,給定一個target,返回array數組裡面// 第一個大於或等於target的元素下標,如果沒有合法結果則返回len(array)func FirstGreaterOrEqual(array []int, target
  • 數據結構與算法-2
    >(4)堆棧、(優先級)隊列(5)map、set(6)樹、圖(7)遞歸、分治、回溯(8)貪心算法(9)BFS、DFS(10)剪枝(11)二分查找(12)Trie樹(13)位運算(14)動態規劃(15)併查集
  • 從經典算法題看時間複雜度
    對數 是冪運算的逆運算。假如 x=βyx=βy,那麼就有 y=logβxy=logβx。二分查找對數時間最典型的算法應該就是二分查找了。例如這道題 搜索插入位置。二分查找的基本思想:將查找的鍵和子數組的中間鍵作比較:如果被查找的鍵小於中間鍵,就在左子數組繼續查找如果大於中間鍵,就在右子數組中查找否則中間鍵就是要找的元素
  • 二分查找,沒那麼簡單!
    算法刷題注意事項算法刷題更要注重質量,最好對做過的每一道題都要分析背後的基本原理,然後寫出準確的代碼。如果當時沒有想出好的解法,是因為什麼?經過多次調試才過的題目,出錯的地方又是在哪裡?如果以上這些問題不注意,過段時間再做這道題,還是可能不會,還是可能要看答案,反反覆覆,不管作多少遍,算法的技能始終進步不大。所以,算法刷題一定要多思考,多總結,一定要注重質量!
  • 數組的基2分裂法
    同理,數組C(x)也可進行相同的抽取,當所有的步驟完成時,得到的新的數組D(x)={x0,x8,x4,x12,x2,x10,x6,x14,x1,x9,x5,x13,x3,x11,x7,x15};錯略一看,如果編寫程序,用迭代的方法最為合適,但是笨蛋熊在程序編寫過程中,遇到了不少的問題,首先是元數組抽取一次後分裂為2個數組,新的2個數組再次抽取後得到4個數組,一直往下分裂,直至最終的數組長度為2時停止
  • 「二分查找」之我見!今天刷一道leetcode算法!
    來自:碼農田小齊算法將是我今後更新的重點,因為我個人非常喜歡。。而且面試考它啊!
  • 一文帶你從代碼的角度詳細搞懂什麼是二分查找樹
    一、二分查找二分查找應該應熟悉了吧?要在順序儲存結構中進行查找,跟最中間的數據進行比較,小的話去前半部分進行查找,否則,去後半部分去查找,其實,可以用迭代和遞歸分別來實現二分查找。1、迭代法  首先,用迭代法來實現,代碼如下:// 二分查找法,在有序數組arr中,查找target// 如果找到target,返回相應的索引index// 如果沒有找到target,返回-1
  • 專欄:ACM算法面面觀[4]-快速冪
    快速冪不僅本身非常常見,而且後續很多算法也都會用到快速冪。讓我們先來思考一個問題:7的10次方,怎樣算比較快?方法1:最樸素的想法,7*7=49,49*7=343,... 一步一步算,共進行了9次乘法。這樣算無疑太慢了,尤其對計算機的CPU而言,每次運算只乘上一個個位數,無疑太屈才了。這時我們想到,也許可以拆分問題。