Featured image of post 從LeetCode學演算法 - 112 Binary Search (6) / Newton’s Method

從LeetCode學演算法 - 112 Binary Search (6) / Newton’s Method

0367. Valid Perfect Square (Easy)

寫在前面

目前0元挑戰賽 的活動已經開始囉!
只要修課後三個月內完課且成功錄取新工作並撰寫心得,
課程就會全額退費喲!請使用下面連結看更詳細的規則說明:

同時,還有限時更便宜的套組3490 (再便宜500呦!限額10名!)
新年限量組合🎁 零元求職挑戰賽 Python 組|從 LeetCode 學演算法
https://hiskio.com/packages/ZXwe2p9yv

零元求職挑戰賽 Python 組|從 LeetCode 學演算法 + 面試成功指南 http://bit.ly/zeroclg

抽獎活動還在繼續累積人數(現在好像沒有人想抽XD)
Python Taiwan的連結第100篇的文章 底下,
公開分享到你的臉書、按讚該篇文章、並留言告訴我說,
「你最喜歡這一整個系列的哪一篇?為什麼?」或
「除了從LeetCode學演算法系列以外,
你還想要看到關於什麼方向的文章?」

超過20則留言的話 (有完成以上步驟的才算),我們就抽一組
「從Leetcode學演算法|進階篇」+「從Leetcode學演算法|面試篇」
課程的免費兌換券進行贈送!

期限嘛…就延長到滿人數吧XDD (不然也沒辦法哈哈)

容筆者工商一下,
「從Leetcode學演算法|進階篇」 開放預購啦!
這次選了40道難度加深的LeetCode題目,
同樣也會細部解說對應的技巧及須要掌握的演算法!
同時這次購買進階篇的話,
額外還加贈**「從Leetcode學演算法|面試篇」** !
當中包含了面試準備須知分享及訪談國內外不同經驗的工程師
讓你不論是想走前端/後端/一般軟工 或者是想找國外的工作
初學想轉職 還是正在工作 ,都能夠從中得到收穫呦!
有興趣的朋友可以使用下面的早鳥優惠~
「從Leetcode學演算法|進階篇」+「從Leetcode學演算法|面試篇」
https://bit.ly/leetcodeadv

Question

Given a positive integer num, write a function which returns True if num is a perfect square else False.

Follow up: ** Do not** use any built-in library function such as sqrt.

Example 1

1
2
Input: num = 16
Output: true

Example 2

1
2
Input: num = 14
Output: false

Constraints

  • 1 <= num <= 2^31 - 1

分析/解題

給定一個正整數num,試檢驗num是否為完全平方數(也就是可以分解成兩個完全相等的數相乘)。
請不要使用內建的sqrt或類似的函式。

雖然被封印了平方根函數,但已經看到標題的讀者應該不難猜想到,
我們可以運用一般的二分逼近法,
去將目標(我們先稱之為x)的範圍縮小。
當找到 x² 和num相等時,就表示num是完全平方數(回傳True),
否則一路找到兩個邊界交叉時,表示num不存在整數平方根,
我們就可以回傳False。

那麼,有沒有再更厲害一點的方法呢?
這邊來介紹一下一個著名的方法,名為牛頓法(Newton’s Method)。
我們在求x² = num當中的x,(下面方便起見,將num寫成n)
其實相當於求 y = f(x) = x² - n在y = 0時的正根。
假定r是這個根,我們先取一個任意一個比較大的值x0,
那麼它對應到的座標應該是(x0, f(x0))。
對於這個點做曲線的切線的話,應該可以得到切線方程式:
y = f(x0) + f’(x0) (x-x0) 
也就是y = ax +b嘛! a就是在x0上的切線斜率,
而x代入x0的話可以驗證y = f(x0),
所以這個方程式可以表達該切線沒錯。

我們將這個切線叫做L,
那麼L和x軸的交點的x座標,應該就和r更近囉!
(想像一下拋物線的斜度會越來越平,所以切線和x軸的交點,
比起拋物線和x軸的交點來說應該會大一點)
我們將後面和x軸的交點位置叫做x1
那麼就可以計算其斜率為 f(x0) / (x0-x1) = (x0²-n)/(x0-x1)
同時也等於f(x)的導數(就是對其微分)f’(x) = 2x
因此,2x0 = (x0²-n)/(x0-x1) -> 2x0(x0-x1) = x0²-n
繼續化簡:
-> 2x0²-2x0x1=x0²-n
-> x0²+n = 2x0x1
-> x1 = (x0 + n/x0) / 2

每次取得一個更接近的x來逼近r的位置

我們可以再用這個x1再取得在f(x)上的點,接下來再畫切線,
重復上面的動作。
當我們每次用前面這個算式迭代,就可以取得一個新的x,
理論上它會更接近於r。(x1, x2, x3, …)
由於要取的是整數,所以我們在計算的時候,
就將所有的算式中的除法都取整數,
這樣算出來的狀況,就會變得有機會更小。
所以在運用到實際的計算時,
我們可以設定一個迴圈,將x直接先定為num,
當x² > num時,就持續迴圈,
那麼在x持續縮小的狀況下,
最終要嘛縮小到剛好得到一個正整數的平方根,
要嘛就是x²已經小於num了,我們就不用繼續往下做啦!

依此,寫成程式碼如下:

Java Java的部分,請留意到大小問題,我們應該要使用long來宣告變數。

Python

Python部分則請不要忘記整數除法是”//”。

面試實際可能會遇到的問題

「時間/空間複雜度?」
(時間複雜度難以界定,最差應該是O(num),(但二分法也是)
平均應該優於O(log(num)),這可能也取決於初始值的取法;
空間複雜度則為O(1))

相似及延伸:
本題還有其他的解法可以參考以下連結
https://leetcode.com/problems/valid-perfect-square/discuss/130010/Python-4-Methods-with-time-testing

Special Thanks suggestions/corrections from viewers:
(歡迎提供意見協助更正歐~)

從LeetCode學演算法,我們下次見囉,掰~

想看看其他題目嗎?
歡迎從第零篇 找你想要的!

同時追蹤粉專以獲得更多新文章的通知:
跟著Desolve學程式

另外,「從Leetcode學演算法|基礎篇」 已經回原價囉,
先前的優惠碼會失效,但這邊仍然為讀者保留了300塊的折抵,
雖然整捆一起買總體比較划算 (差290而已就有整組了阿XD),
如果你還是想先試試基礎課的話,
300元折抵的連結 在這邊:
https://bit.ly/1desolve

共發表了 171 篇文章 ‧ 總計 311.6k
使用 Hugo 建立
主題 StackJimmy 設計