1:二分查找

思路:要实现一个sqrt函数,可以使用二分法,首先确定一个范围[begin, end],这个范围的中间数mid,看mid的平方是否等于x,如果相等,则返回mid,如果不等则缩小[begin,end]的范围,为原来的一半。这里的初始范围可以是[1, x],也可以是更精确一些的[1, (x/2) + 1]。(因 (x/2) + 1 的平方等于 x+1+(x^2/4),它一定大于x,所以,x的平方根一定在[1, (x/2) + 1]范围内)

题目中给出的函数原型是int mySqrt(int x)。参数和返回值都是整数。这里稍微扩展一下,将函数原型改为double mySqrt(int x)。解题思路还是一样的,但是浮点数因精度的原因,无法判断两个浮点数是否完全相等,只能说两者的差值绝对值小于某个精度,所以在二分查找时,需要一定的技巧,具体的代码如下:

double mySqrt_binarysearch(int x)
{if(x <= 0) return 0;double begin = 1;double end = x/2+1;double mid, lastmid;mid = begin + (end-begin)/2;do{if(mid < x/mid) begin = mid;else   end = mid;lastmid = mid;mid = begin + (end-begin)/2;}while(ABS(lastmid-mid) > FLT_MIN);return mid;
}

上面的代码中,逐步缩小[begin,end]的范围,通过判断上次的lastmid与本次的mid的差值绝对值是否在精度之内,来决定是否继续寻找下去。

2:牛顿迭代法

上面的实现方法只能说是中规中矩,但是实现sqrt有更牛逼的方法,就是牛顿迭代法。该方法就是由我们熟知的牛顿提出的。具体思想可以自行搜索。简而言之,如下图:

x^2 = a的解,也就是函数f(x) = x^2 – a与x轴的交点。可以在x轴上先任选一点x0,则点(x0, f(x0))在f(x)上的切线,与x轴的交点为x1,它们满足切线的方程:f(x0)=(x0-x1)f’(x0),可得x1更接近最终的结果,解方程得到:

x1 = (x0 + (a/x0))/2。以x1为新的x0,按照切线的方法依次迭代下去,最终求得符合精确度要求的结果值。它的实现代码如下:

double mySqrt_newton(int x)
{if(x <= 0) return 0;double res, lastres;res = x;  //初始值,可以为任意非0的值do{lastres = res;res = (res + x/res)/2;}while(ABS(lastres-res) > FLT_MIN);return res;
}

使用牛顿法解决sqrt的效率非常高,关于效率比较可参见本文最后一节。牛顿法的效率很大程度上取决于初始值的选取,这就引出了下一节。

3:神迹

下面这段代码出自《雷神之锤》,至今尚未找到该代码的真正作者,代码如下:

float InvSqrt(float x)
{float xhalf = 0.5f * x;int i = *(int*)&x; i = 0x5f375a86 - (i>>1); x = *(float*)&i;x = x*(1.5f-xhalf*x*x); x = x*(1.5f-xhalf*x*x); x = x*(1.5f-xhalf*x*x);return 1/x;
}

它本质上还是使用的牛顿迭代法,真正牛逼的地方在于它初始值的选择,0x5f375a86这个魔法数字的由来尚不可知,该算法的具体原理及其背景可以参见维基百科,不再赘述。

要注意的是,上面算法使用的是float和int类型,实验可知他们不能替换为double和long。

4:效率

使用下面的代码,测试上述三种方法,以及系统默认方法的效率:

int main(int argc, char **argv)
{clock_t begin, end;int num = atoi(argv[1]);double res;int i;int loopcnts = 1000000;begin = clock();for(i = 0; i < loopcnts; i++)res = mySqrt_binarysearch(num);end = clock();printf("mySqrt_binarysearch(%d) = %f, spent time is %f\n", num, res, (double)(end-begin));begin = clock();for(i = 0; i < loopcnts; i++)res = mySqrt_newton(num);end = clock();printf("mySqrt_newton(%d) = %f, spent time is %f\n", num, res, (double)(end-begin));begin = clock();for(i = 0; i < loopcnts; i++)res = InvSqrt(num);end = clock();printf("InvSqrt(%d) = %f, spent time is %f\n", num, res, (double)(end-begin));begin = clock();for(i = 0; i < loopcnts; i++)res = sqrt(num);end = clock();printf("system sqrt(%d) = %f, spent time is %f\n", num, res, (double)(end-begin));}

测试结果如下:

mySqrt_binarysearch(65535) = 255.998047, spent time is 3437535.000000
mySqrt_newton(65535) = 255.998047, spent time is 659694.000000
InvSqrt(65535) = 255.998047, spent time is 65902.000000
system sqrt(65535) = 255.998047, spent time is 82605.000000

可见,二分法最慢,普通的牛顿迭代法次之,神迹代码要比系统库的还要快一些。

Ps:谨以此文,给予那些不知天高地厚的程序员们,当头棒喝!

参考:

https://zh.wikipedia.org/wiki/%E7%89%9B%E9%A1%BF%E6%B3%95

https://zh.wikipedia.org/wiki/%E5%B9%B3%E6%96%B9%E6%A0%B9%E5%80%92%E6%95%B0%E9%80%9F%E7%AE%97%E6%B3%95

http://kb.cnblogs.com/page/189867/

三种sqrt函数实现相关推荐

  1. SQL 中 Rank、row_number、dense_rank 三种排序函数的区别

    现有一张工资表,需要对其进行排名,工资相同的人并列排名,然后再排名,很多刚接触的小伙伴估计第一时间想到Rank()函数或row_number() 函数,但是结果出来后并不是自己想要的,在这里就给大家介 ...

  2. PostgreSQLl三种圆整函数对比

    三种圆整函数: ceil(val):取val向上圆整的整数,不是是否大于.5,即1.2-->2, 1.8-->2,ceil有屋顶,天花板意思 floor(val): 取val向下圆整的整数 ...

  3. SQL中的三种count()函数

    数据库的使用中,Count() 函数用于统计数据表的记录,返回匹配指定条件的行数. 三种Count() 函数 count(1).count(*).count(列名) 区别 1. count(1).co ...

  4. java光标移动函数_文件内光标的移动 函数基础 定义函数的三种形式 函数的返回值 调用方式...

    # with open(r'a.txt', 'r', encoding='utf-8')as f: # data1=f.read() # print('>1>:',data1) # pri ...

  5. SQL,三种排名函数,用作排名使用

    前言: 在某些应用场景中,我们经常会遇到一些排名的问题,比如按成绩或年龄排名.排名也有多种排名方式,如直接排名.分组排名,排名有间隔或排名无间隔等等,这篇文章将总结几种MySQL中常见的排名问题. 创 ...

  6. 三种开窗函数详细用法,图文详解

    开窗函数的详细用法 一,开窗函数的语法 二,从聚合开窗函数sum(score) over(partition by name )讲起 三,开窗函数之first_value,last_value,lea ...

  7. mybatis中LIKE模糊查询的几种写法以及注意点(亲测第三种CONCAT()函数的)

    mybatis中对于使用like来进行模糊查询的几种方式: (1)使用${...} 注意:由于$是参数直接注入的,导致这种写法,大括号里面不能注明jdbcType,不然会报错 org.mybatis. ...

  8. STM32三种延时函数实现方法

    想学习单片机的同学可以关注.私信我或者在评论区回复我要入门.在51入门的时候我们第一个实验就是点亮LED灯,如果没有延时,我们就很难看到亮灭效果. 1. STM32延时函数概述 在产品开发的过程中我们 ...

  9. 向函数传递结构的三种方式

    向函数传递结构的三种方式 零.写在前面 在程序设计的过程当中,我们经常会涉及到选择数据的类型,在很多情况下,单单是普通的变量和数组并不足以满足我们的需求,这时候,C语言为我们提供了一种计较好用的数据类 ...

最新文章

  1. TPC性能测试及发布
  2. 大家都说 Java 反射效率低,为什么呢?
  3. redirect_uri参数错误解决方法
  4. OSS全球传输加速开启公测,助力企业业务全地域覆盖...
  5. centOS7 安装redis-3.2.6
  6. Ireport制作过程
  7. 【AI面试题】什么是数据不平衡,如何解决
  8. java登录验证技术,login_required -- 登录验证
  9. 你还会不会选择我(刘墉 )
  10. ubuntu MySQL数据库输入中文乱码 解决方案
  11. 微信小程序如何修改单页面背景色
  12. CountDownLatch--等待多线程计数器
  13. 建筑计算机辅助设计证书,学术讲座:计算机辅助设计绘图员(建筑类)职业技能鉴定...
  14. DataPipeline亮相“2021科技助力湾区数字金融发展峰会”,解锁“实时数据管理”密码
  15. IT业界新闻资讯网站推荐
  16. 怎么把aac转化为mp3,aac转mp3的3个方法
  17. Python学习之线性图
  18. C#使用Topshelf和Quartz开发处理定时任务的Windows服务程序
  19. RK3368 Recovery界面旋转方向调试
  20. 用Vue制作简易音乐播放器(超详细)

热门文章

  1. 【BZOJ4237】稻草人
  2. 001_汽车之家,新浪和360之间的交流
  3. 本机连接虚拟机Oracle时报错的解决办法
  4. Oracle中DUMP转储方法
  5. MongoDB应用篇
  6. SpringBoot三种获取Request和Response的方法
  7. python 彩票 遗漏值_荐Python遗漏知识点一
  8. SwiftUI之深入解析高级动画的时间轴TimelineView
  9. 最短路径(Shortest Paths)
  10. python获取计算机IP、mac地址、计算机名