LeetCode 69: 牛顿法求平方根
题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems
供稿: 魏成
设 x
是一个正数,牛顿法求 x
的平方根的迭代公式为
r
0
=
x
r
n
+
1
=
r
n
+
x
/
r
n
2
\begin{aligned} r_0 &= x\\ r_{n + 1} &= \frac{r_n + x / r_n}{2} \end{aligned}
r0rn+1=x=2rn+x/rn
牛顿法收敛速度很快。粗略来说,每次迭代都使得小数点后的有效数字位数翻倍。在本题中,只需求 sqrt(x)
的整数部分,所以我们也尝试只用整数运算来解决。 C 语言代码如下:
int mySqrt(int x) {
int r;
if(x == 0)
return x;
r = x;
while(r > x / r)
r = r + x / r >> 1;
return r;
}
正确性验证:对非负整数而言, a > b / c
和 a * c > b
是等价的,其中 /
是整除,因此只需验证当 r > sqrt(x)
时确实有 (r + x / r) / 2 + 1 > sqrt(x)
。注意到后者使用了两次整除,所以验证时要稍微仔细些。以下记 /
为整数除法,而用分数线表示通常的分数:
(
r
+
x
/
r
)
/
2
+
1
⩾
r
+
x
/
r
2
−
1
2
+
1
=
r
+
x
/
r
+
1
2
>
r
+
x
r
2
>
x
.
(r + x / r) / 2 + 1 \geqslant \frac{r + x / r}{2} - \frac{1}{2} + 1 = \frac{r + x / r + 1}{2} > \frac{r + \frac{x}{r}}{2} > \sqrt x.
(r+x/r)/2+1⩾2r+x/r−21+1=2r+x/r+1>2r+rx>x.
一般情况这样就足够了,但提交时还是会报错,因为本题中 x
的取值范围是 0
到 INT_MAX
,当 x
取到 INT_MAX
的时候,第一次循环试图计算 INT_MAX + 1
,从而造成整数溢出。因此可以对初始值的选取稍加调整:
if(x < 3)
return x + 1 >> 1;
r = x >> 1;
while(r > x / r)
r = r + x / r >> 1;
return r;
对 LeetCode 441: 排列硬币 也有类似的解法:
int arrangeCoins(int n){
int x;
if(n < 3)
return 1;
x = n;
do
x = (x >> 1) + n / x;
while(n / x < x + 1 >> 1);
if(x % 2 == 0 && n / (x + 1) < x >> 1)
return x - 1;
return x;
}