Sqrt(x)
沒錯,有幾天沒寫LeetCode了,就遇上了sqrt函數的編寫,這是不是很簡單呢?於是我很快的想出了遍歷的方法,但是馬上又想到了若是一個最大的整數位測試用例呢?那麼效率太低了吧,於是我以效率爲出發點又想到了二分法,使用二分法自己測試了幾個,感覺還可以,於是就提交了,代碼如下:
class Solution
{
public:
int mySqrt(int x)
{
//二分法
assert(x >= 0);
//1.特殊情況
if (x == 0 || x == 1)
{
return x;
}
//2.一般情況
int left = 0;
int right = x;
int mid = (left + right) / 2;
while (mid*mid > x || (mid+1)*(mid+1) <= x)
{
if (mid * mid > x)
{
right = mid;
}
else if ( (mid + 1) * (mid + 1) <= x)
{
left = mid;
}
mid = (left + right) / 2;
}
return mid;
}
};
測試用例卡在了2147395599上面,結果如下:
出現這種結果說明二分法還是不夠優化,那麼我們所能想到的辦法還有什麼呢?我感覺真的是想不出來,於是我在網上看了一些文章,將關於sqrt函數的問題,於是我找到了一種求解方法,利用到的思想是”牛頓迭代法快速尋找平方根“,其思想是利用高數中的思想:求 X^2 - a = 0 的根,這些思想若是沒有學習過真的是想不出來的,我是參考一篇文章,所以最後我會將這篇文章貼出來,分享給大家。
使用牛頓迭代快速尋找平方根的思想,我們可以寫出下列代碼:
class Solution
{
public:
int mySqrt(int x)
{
//牛頓迭代法
assert(x >= 0);
if (x == 0 || x == 1)
{
return x;
}
float val = x; //最終
float last; //保存上一個計算的值
do{
last = val;
val = (val + x / val) / 2;
} while (abs(val - last) > 0.01); //因爲這個題的最終結果是整數,所以我們可以將精度提高點
int ret = (int)val;
if (ret*ret > x)
{
return ret - 1;
}
else
{
return ret;
}
}
};
這個函數最終被接收了,結果如下:
其實到這裏還沒完呢?有些人真的是大神,他們只要兩步就可以求出一個數的平方根,他的思路我還沒弄明白,因爲源碼是某遊戲引擎的代碼,不過這裏還是將他的代碼貼出來,跟大家分享一下:
class Solution
{
public:
int mySqrt(int x)
{
//數學大神根據牛頓迭代法來求推導出的神奇方法
assert(x >= 0);
if (x == 0 || x == 1)
{
return x;
}
float tmp = x;
float xhalf = 0.5f*tmp;
int i = *(int*)&tmp;
i = 0x5f375a86 - (i >> 1); // 這一步是關鍵
tmp = *(float*)&i;
tmp = tmp*(1.5f - xhalf*tmp*tmp);
tmp = tmp*(1.5f - xhalf*tmp*tmp);
tmp = tmp*(1.5f - xhalf*tmp*tmp);
int ret = 1 / tmp;
if (ret*ret > x)
{
return ret - 1;
}
return ret;
}
};
這種方法是正確的,因爲它是遊戲引擎中的源碼,所以不用驗證都可以知道其正確性。
我個人覺得這種類型的題真的是很吃數學功底,也道題很徹底的讓我感受到了數學的強大,讓我深受感觸。下面我就給出我看過的文章的出處:參考文章