一般使用两种方法,二分法和牛顿迭代法
一般会让有两种求解结果,一种是求解其整数部分,另一种是求解浮点数并给出精度
二分法
思路
x的平方根的整数部分肯定是在0~x之间的,所以我们可以直接将其转换为在以0开头的有序数组中使用二分查找定位该数字(这数值就是mid:每次二分查找的中间值),那么mid^2一定是最接近x的。
需要定义的变量
l :表示左边值
r:表示右边值
mid:临时存储 l+r /2的中间值
n值的临界条件判断
当mid * mid > x,则 mid 取 l 到 mid -1的中间数,继续循环,直到mid * mid < x,mid就是要找的数 ;
当mid * mid <= x ,则mid取 mid +1 到 r 的中间数,并将mid 赋值给n ,直到两边界限重合,n就是要找的数。
时间复杂度:O(logN)
劣势:数值太大容易超过限制,可以做下修改
将临界的判断条件改成
mid > x / mid,则 mid 取 l 到 mid -1的中间数,继续循环,直到mid < x / mid,mid就是要找的数
当mid <= x / mid ,则mid取 mid +1 到 r 的中间数,并将mid 赋值给n ,直到两边界限重合,n就是要找的数。
//整数二分
//边界划分[l, mid - 1] [mid, r]
int bsqrt(int x)
{
if(x == 0||x == 1) return x;
int r = x;
int mid = 0;
int l = 0;
while(l < r)
{
mid = (int)(l/2.0 + r/2.0 + 0.5);//防止超出int范围
if(mid <= x / mid) l = mid;
else r = mid - 1;
}
return l;
}
直接浮点数求解二分开根号,注意设置精度
//使用浮点数二分
double bsqrt(double x)
{
const double esp = 1e-6;//精度
double r = x, l = 0.0;
while(r - l > esp)
{
double mid = r / 2.0 + l / 2.0;//防止超出范围
if(mid - x / mid >= esp) r = mid;//防止超出范围
else l = mid;
}
return l;
}
牛顿迭代法
求n的平方根,即计算x2=nx^{2}= nx2=n的解,令f(x)=x2−nf(x)=x^{2}-nf(x)=x2−n,相当于求解f(x)=0f(x)=0f(x)=0的解。
1.首先取猜测x0x_0x0(例如可以设初次猜测值为x/2x/2x/2),如果x_0不是解,做一个经过(x0,f(x0))这个点的切线,与xxx轴的交点为x1x_1x1。 同理,如果x1x_1x1不是解,做一个经过(x1,f(x1))(x_1,f(x_1))(x1,f(x1))这个点的切线,与x轴的交点为x2x_2x2。 以此类推。 以这样的方式得到的xi会无限趋近于f(x)=0的解。
2.判断xix_ixi是否是f(x)=0f(x)=0f(x)=0的解有两种方法: 第一种是直接计算f(xi)f(x_i)f(xi)的值判断是否为0,第二种是判断前后解xix_ixi和xi−1x_{i-1}xi−1是否无限接近(误差精度)。经过(xi,f(xi))(x_i, f(x_i))(xi,f(xi))这个点的切线方程为f(x)=f′(xi)(x−xi)+f(xi)f(x) = f'(x_i)(x - x_i)+f(x_i)f(x)=f′(xi)(x−xi)+f(xi) ,其中f′(x)f'(x)f′(x)为f(x)f(x)f(x)的导数。(本例中为2x2x2x)令切线方程等于0,即可求出x(i+1)=xi−f(xi)/f′(xi)x(i+1)=x_i - f(x_i) / f'(x_i)x(i+1)=xi−f(xi)/f′(xi)。在此题中,化简可得到迭代公式xi+1=xi−(xi2−n)/(2xi)x_{i+1}=x_i - (x_i^{2} - n) / (2x_i)xi+1=xi−(xi2−n)/(2xi)
整数部分
#define error 1e-9//误差精度
class Solution {
public:
//inline double df(int& x, double& x0){return x0 - (x0 * x0 - x)/(2 * x0);}//化简一下(x0 + x / x0) / 2
inline double df(int& x, double& x0){return (x0 + x/ x0) / 2;}
int mySqrt(int x)
{
if(x == 0 || x == 1) return x;
double x0 = x / 2.0;
double x1 = df(x, x0);
while (abs(x0 - x1) >= error)
{
x0 = x1;
x1 = df(x, x0);
}
return (int) x1;
}
};
化简下
class Solution {
public:
int mySqrt(int x) {
if(x == 0 || x == 1) return x;
long res = x;
while (res * res > x)
{
res = (res + x / res) / 2;
}
return res;
}
};
返回浮点数
#define error 1e-9//误差精度
class Solution {
public:
//inline double df(int& x, double& x0){return x0 - (x0 * x0 - x)/(2 * x0);}//化简一下x0/2 + x/ (2x0)
inline double df(int& x, double& x0){return (x0 + x/ x0) / 2;}
double mySqrt(int x)
{
if(x == 0 || x == 1) return x;
double x0 = x / 2.0;
double x1 = df(x, x0);
while (abs(x0 - x1) >= error)
{
x0 = x1;
x1 = df(x, x0);
}
return x1;
}
};