1.数学原理
求一个数c的平方等价于求方程 x 2 = c x^{2} = c x2=c 的解,方程解的问题可以转换为函数零点问题。即求函数 f ( x ) = x 2 − c f(x) = x^{2}-c f(x)=x2−c的零点。
零点定理:若f(a)f(b)<0则∃m ∈ \in ∈[a,b],有f(m) = 0
由零点定理可知,只需估计a,b的值,令m=(a+b)/2,然后不断二分就可以得到更加小的区间[a,b]。使得 f ( m ) ≃ 0 f(m) \simeq 0 f(m)≃0,此时的m就是所求。
现在问题就是如何选取a,b的值,上述可知:
1.a,b,m必须满足不等式
a
≤
m
≤
b
a\le m \le b
a≤m≤b
2.由于开方的结果必然为正整数 (即
m
≥
0
m\ge0
m≥0),因此可以取下界
a
=
0
,
有
f
(
a
)
=
−
c
<
0
a=0,有f(a) = -c<0
a=0,有f(a)=−c<0
3.因为f(a)<0,则f(b)>0才有f(a)f(b)<0,所以考虑满足f(b)>0的点。考虑b=c有,当
c
≥
1
时
,
f
(
b
)
=
c
2
−
c
>
0
可
取
,
当
0
≤
c
<
1
时
f
(
b
)
<
0
不
符
合
c\ge1 时,f(b) = c^{2}-c>0可取,当0\le c<1时f(b) <0不符合
c≥1时,f(b)=c2−c>0可取,当0≤c<1时f(b)<0不符合。可见b要取一个大于c的值,不妨令b = c+k(k>0) 使得
f
(
b
)
=
c
2
−
c
+
k
>
0
=
>
(
c
−
1
2
)
2
+
(
k
−
1
4
)
>
0
f(b) = c^{2}-c+k>0 => (c-\frac{1}{2}) ^{2}+(k-\frac{1}{4})>0
f(b)=c2−c+k>0=>(c−21)2+(k−41)>0
=
>
k
>
1
4
=>k>\frac{1}{4}
=>k>41
不妨就令k = 0.25 故 b = c+k = c+0.25
2.代码实现
#include <iostream>
#include<iomanip>
#include <stdlib.h>
#include <cstring>
#include <cfloat>
//控制精度,也可以直接使用double中定义的常量: DBL_EPSILON
#define PRESCION 0.00001
using namespace std;
//零点定理二分开平方
double sqrt(double c)
{
double a = 0,b = c+0.25;
double m;
while(1)
{
//1.二分取区间[a,b]的中点
m = (a+b)/2;
//2.使用区间长度控制精度
//注意:这个判断必须写在3的前面,否则a或者b已经更新为m必然会有m-a=0||b-m=0,导致只循环1次就退出
if(b - m < PRESCION||m - a <PRESCION)
break;
//3.使用零点定理f(m)f(b)<0来选择区间 左区间:[a,m),右区间:(m,b]
//或由于0<a<m<b,即a,m,b都大于0,只往0+的方向趋近也可直接比较m^2与c的大小来选择区间 if((m*m - c)<0)
if((m*m - c)*(b*b - c)<0)
a = m;
else
b = m;
}
return m;
}
int main()
{
double n;
cin>>n;
//控制输出位数
cout<<setiosflags(ios::fixed)<<setprecision(10)<<sqrt(n)<<endl;
return 0;
}