1. 程式人生 > >LeetCode[Math]----Sqrt(x)

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

直觀感受,牛頓法的迭代效率應該會優於二分法,畢竟牛頓法還考慮到了導數,是一種非線性的查詢。不過實際提交執行發現,三個程式碼的執行效率並沒有太大差別。