关于平方根运算的算法

这段日子,空闲的时候在看SICP(Structure and Interpretation of  Computer Programs),发现满有意思的。
刚开始就看到了一个求平方根的算法。满脑子搜寻,好像从小就没学过如何计算平方根,只知道平方根的定义。于是就打算记录下来。

用牛顿法求解平方根:

输入:x
输出:y
要求:(y * y - x) < 0.001   (注:因为平方根运算一般都牵涉到一个精度的问题)
算法:
1. 猜测一个值作为y
2. 如果y的精度不够,则用如下公式改进y:
    y = (y + x/y) / 2;
    然后重复第二步;
    如果y的精度够了,就输出y;

不知道上面的表达是不是够清楚了。下面给出我的程序吧。

#include <iostream>

using namespace std;

double my_abs(double y)
{
     if(y < 0)
          return (-y);
     else
          return y;
}

bool good_enough(double guess, double x)
{
     return ( my_abs( (guess * guess) - x) < 0.001);
}

double try_sqrt(double guess, double x)
{
     if(good_enough(guess, x))
          return guess;
     else
          return try_sqrt( (guess + (x / guess)) / 2.0, x);
}
double my_sqrt(double x)
{
     return try_sqrt(1.0, x);
}

int main()
{
     cout << my_sqrt(2) << endl;
     return 0;
}

当然,可以看到,这里对于最初的那个猜测的值,取的是1.0,关于这个初始值的取法,不知道有没有好的方法。另外还有一个问题就是关于如何判断是否够精确了。这里的算法只是判断该值的平方与那个x的差值是否小于0.001。还有一种判断方法就是比较上一次的结果和这次的结果的差值,这个方法与上面的那个方法的差别我还没考虑过。

最后呢,还有一个就是与标准库的sqrt()函数的比较问题。
上面那个程序的运行结果如下:
1.41422
而标准库的结果是:
1.41421
不知道标准库里是用什么算法计算的。

另外,如果把那个判断精度的0.001改成0.0001,好像结果没有变化。但如果改成0.01的话,就明显看出结果的误差,这个算法的结果是:1.41667。另外,如果算36的平方根的时候,取0.01作为精度的话,结果就有问题了,这个算法的结果是:6.00025,如果取0.001作为精度,那结果就正确了,嘻嘻~

等那天有空再去看看标准库的算法吧,怀疑可能就是判断精度的问题上有区别。呵呵。

哦,还有一个需要记录的,就是看SICP上的说法,求平方根只是牛顿法的一个特例,它还有求立方根的算法,其普遍形式好像是一个求解方程的算法,等我看了再作记录吧。

补充:
没解决问题的情况下,总觉得心有不甘,于是我就又试了一下,这次取相邻两次计算值的差来作为精度的确定,发现结果就和上面的不一样了,还是先贴程序:

bool good_enough2(double guess1, double guess2)
{
     if( my_abs( guess1 - guess2) < 0.01)
         return true;
     else
         return false;
}

double try_sqrt2(double guess, double x)
{
     double next_guess = (guess + (x / guess)) / 2.0;
     if(good_enough2(next_guess, guess))
         return next_guess;
     else
         return try_sqrt2( next_guess, x);
}

double my_sqrt2(double x)
{
     return try_sqrt2(1.0, x);
}

注意到上面我用红色标注的那个0.01了吧?在前一种算法中,需要0.001才能达到正确精度,而这个算法只需要0.01就可以达到精度了。当然,我这个精度比较是通过求36的平方根来算的。第二个算法里用0.01就能得到正确的值,而用0.1的话,结果就和第一个算法用0.01的结果一样了。
不过这里面的缘由似乎也很简单吧,因为后面的算法是直接比较结果值,而前者是比较平方后的结果值,这样误差也是有类似于平方关系的吧。就比如现在用的0.01的精度,平方后的精度就是0.0001,比原来的0.001的精度还要高,那结果自然正确。而如果取0.1的话,那么平方之后是0.01,自然就没有第一种那么精确了。

现在我通过测试验证,第二种算法的和标准库的结果是一致的,就比如前面测试的求2的平方根,第一种算法得出的结果是1.41422,而标准库的结果都是1.41421,第二种算法当那个精度值取0.01的时候,结果同第一种算法,而取0.001的时候,结果就与标准库的相同了。

其实想一下也很简单,第二种算法比较的是结果的值,其精度是直接取决于那个精度值的,因此精度可以较高。而第一种算法的那个精度值是结果平方后的,也就是需要精度更为高的值(也就是1之前的0应该更多,呵呵),才能达到高精度。实验证明,第一种算法取0.00001的时候,仍旧是1.41422,而取0.000001的时候,结果就是1.41422了。

好了,平方根算法也研究得差不多了,至少表面样子上已经差不多了。如果从效率上来说,两种算法似乎差不多吧。不过第一种算法“平方之后再比较”应该比第二种算法“直接比较结果”来得慢一些吧,毕竟多了一个乘法运算了。由于数学能力和算法分析能力已经不行了,就不计算复杂度了吧

再补充一下:
刚才这些结果都是用cout的标准输出精度来看的,所以精确度也就这点了。我刚才又找了一下标准库,发现通过std::setprecision() 可以设置输出的精度。结果就发现问题了,就是最后还是发现了结果不同。而且我那个0.001不管设得再小,也好像和标准库的结果不一样,虽然误差很小很小,不过这又使得我怀疑标准库的算法还是有所不同。下面是结果:
1.4142135623730949    算法一
1.4142135623730949    算法二
1.4142135623730951    标准库的算法
上面这个结果是我已经加了好多0的情况下了(具体多少个我也没数过,反正好多,呵呵

就先这样吧。哪天看了标准库的算法,再做补充吧。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值