如何在 C/C++ 中实现整数平方根?

问题:为 int sqrt(int x)实现整数平方根

有很多方法可以计算整数平方根。但是如果你不打扰,你总是可以欺骗编译器(在线判断),但不要在你的面试中这样做!

作弊方式:
好吧,大多数编程语言都有 用于浮点数(单精度或双精度)的sqrt,我们可以简单地编写:

#include <math.h>

class Solution {

public:

int sqrt(int x) {

// add std:: namespace to distinguish from current sqrt()

return (int)std::sqrt(x);

}

};

或者

public class Solution {

public int sqrt(int x) {

return (int)Math.sqrt(x);

}

}

或者

在这里插入图片描述

#include <math.h>

class Solution {

public:

int sqrt(int x) {

return (int)pow(x, 0.5);

}

};

穷举搜索:
人们可能会很快想出一个蛮力搜索。我们只需要尝试从 1 开始的数字,直到这个数字的平方大于输出。

#include <math.h>

typedef unsigned long long ULONG;

class Solution {

public:

int sqrt(int x) {

ULONG a = 1;

while (a * a <= x) a ++;

return --a;

}

}

请注意,我们定义了一个unsigned long long类型来保存a * a 的中间结果, 这样结果就不会溢出(对于 32 位整数来说太大了)。

好吧,以上似乎不太可能在网上判断中接受。但令人惊讶的是,这是一个公认的解决方案。这是为什么?的 int 类型是32位有符号整数,其给出的最大值2 ^ 31 - 1 = 2147483647。这个的平方根只是 46340.9,这意味着最多46341次迭代,我们有正确的整数根。这在现代处理器中是微不足道的,可以在百万秒内完成。

改进:

我们注意到 a * a 很费计算资源,如何摆脱它?
在这里插入图片描述
因此,我们只需将2 添加到变量 delta 并将其添加到当前变量 a 即可得到a + 1的平方 。

#include <math.h>

typedef unsigned long long ULONG;

class Solution {

public:

int sqrt(int x) {

ULONG a = 1;

ULONG delta = 3;

while (a <= x) {

a += delta; // (a + 1)^2

delta += 2;

}

return delta / 2 - 1;

}

}

上述改进去除了乘法并仅使用加法,这通常在一些时间不是关键问题的 PC 板中实现。

二分查找:
上面的算法复杂度是O(n)并且当给定的整数很大时它仍然很慢。二分查找可以将其缩短为
在这里插入图片描述
二分搜索是最重要的算法之一,为了使其工作,它需要连续的搜索空间(例如数字排序)

在整数平方根的域中,我们可以很容易地发现:
在这里插入图片描述小于
在这里插入图片描述任何给定的非负整数。

#include <math.h>

typedef unsigned long long ULONG;

class Solution {

public:

int sqrt(int x) {

ULONG l = 0, r = x;

while (l <= r) {

ULONG mid = l + (r - l) / 2; // (l + r) / 2;

ULONG midmid = mid * mid;

// check if x falls into [mid^2, (mid + 1)^2)

if ((midmid <= x) && (x < (mid + 1) * (mid + 1))) return mid;

if (midmid > x) {

r = mid - 1;

} else {

l = mid + 1;

}

}

}

}

使用二分搜索,每次迭代都会将搜索空间缩小到一半。的 中期=(L + R)/ 2 可能溢出所以我们可以使用 中间= 1 +(R - 1)/ 2 来完成相同的事情。

牛顿法:
二分搜索是一个很大的改进,但找到解决方案仍然太慢。现代处理器实现了硬件平方根算法,其中大部分基于牛顿方程:
在这里插入图片描述
正如我们所看到的,随着牛顿方程的每次迭代,我们找到了更接近的根
在这里插入图片描述
我们有初步的猜测
在这里插入图片描述

在这里插入图片描述
例如,
在这里插入图片描述

牛顿法收敛很快,被认为非常有效。

#include <math.h>

typedef unsigned long long ULONG;

class Solution {

public:

int sqrt_newton(int x) {

if (x==0) return x;

double dividend = x;

double val = x;

double last;

do {

last = val;

val = (val + dividend / val) * 0.5;

} while(abs(val - last) > 1e-9); // precision

return (int)val;

}

}

我们
在这里插入图片描述
用最后一次检查当前迭代
在这里插入图片描述
,如果差异小于例如1e-9 (EPSILON),那么我们认为我们已经获得了所需的精度。

另一个类似的问题是检查给定的整数是否是有效的完美平方。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学习成长分享快乐

您的鼓励是我前进的不竭动力~~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值