LeetCode[Math]----Sqrt(x)
Implement int sqrt(int x)
.
Compute and return the square root of x, where x is guaranteed to be a non-negative integer.
Since the return type is an integer, the decimal digits are truncated and only the integer part of the result is returned.
Example 1:
Input: 4 Output: 2
Example 2:
Input: 8
Output: 2
Explanation: The square root of 8 is 2.82842..., and since
the decimal part is truncated, 2 is returned.
分析:
題目來源於LeetCode 第69題(https://leetcode.com/problems/sqrtx/),據說這道題有好幾種做法,先記錄下今天的方法。
求一個數的平方根,但是不需要求得精確值(不需要小數),只需要得到一個整數值就可以了。
拿到題目後會想,最簡單肯定是給定x,對小於等於x的所有整數遍歷一下就得到了,不過這樣效率比較差,有沒有更好的方法呢?
第一種想法是嘗試利用sqrt(ab) < (a+b)/2的關係,但是好像沒有什麼用。第二種想法是在第一種想法的基礎上發現一個規律:
當x >= t**2 時,只要嘗試小於等於 x/t 的整數就好了,比如當x >= 16時,應該嘗試 x/4 的整數,但是這種思路需要嘗試x的範圍,也不是很方便。最後在第二種想法的基礎上想到可以利用下二分法進行嘗試,時間複雜度為logn,還是可以接受的。
具體思路是,用二分法按mid值的平方大於或小於x來逼近那個整數,而當mid值等於左右區間中間點時,該mid值即為所要的整數。
程式碼:
class Solution(object): def mySqrt(self, x): """ :type x: int :rtype: int """ if x == 0: return 0 if x <= 3: return 1 left, right = 1, x mid = -1 while mid != (left + right) / 2: mid = (left + right) / 2 if mid ** 2 > x: right = mid else: left = mid return mid
2018.11.21補充牛頓法:
之前就聽說過這道題可以用牛頓法來求解。牛頓法可以用來求高階方程的根,我們有兩種方式能得到牛頓法的迭代公式,第一種是對f(x)進行在x0點泰勒展開,忽略掉二階及高階項,令 0=f(x*)≈f(x0)+f'(x0)(x* - x0) ,近似得到 x*≈x1=x0 - f(x0)/f'(x0),再在x1點進行泰勒展開,得到 x2=x1 - f(x1)/f'(x1),由此得到迭代公式 xn_1 = xn - f(xn) / f'(xn_1) ,通過迭代公式可以快速逼近 x* . 第二種方式是通過切線,構建切線,以切線近似f(x), 同樣得到迭代公式,而當切線無限逼近(x*, f(x*)) 時,切線的零點可以近似看做f(x)的零點。當然牛頓法使用有一個前提,就是 f(x) 滿足二階可導,此時牛頓法可以收斂。
按牛頓法寫了原始碼如下:
def mySqrt(self, x):
"""
:type x: int
:rtype: int
"""
if x == 0:
return 0
if x <= 3:
return 1
xn_1, xn = 1000, 8.0
while True:
xn_1 = (xn**2 + x) / (2 * xn)
if int(xn_1) == int(xn):
break
xn = xn_1
return int(xn_1)
在查看了其他部落格程式碼後,優化程式碼如下:
def mySqrt(self, x):
xn = x
while xn * xn > x:
xn = (xn + x / xn) / 2
return xn
直觀感受,牛頓法的迭代效率應該會優於二分法,畢竟牛頓法還考慮到了導數,是一種非線性的查詢。不過實際提交執行發現,三個程式碼的執行效率並沒有太大差別。