三种sqrt函数实现
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函数实现相关推荐
- SQL 中 Rank、row_number、dense_rank 三种排序函数的区别
现有一张工资表,需要对其进行排名,工资相同的人并列排名,然后再排名,很多刚接触的小伙伴估计第一时间想到Rank()函数或row_number() 函数,但是结果出来后并不是自己想要的,在这里就给大家介 ...
- PostgreSQLl三种圆整函数对比
三种圆整函数: ceil(val):取val向上圆整的整数,不是是否大于.5,即1.2-->2, 1.8-->2,ceil有屋顶,天花板意思 floor(val): 取val向下圆整的整数 ...
- SQL中的三种count()函数
数据库的使用中,Count() 函数用于统计数据表的记录,返回匹配指定条件的行数. 三种Count() 函数 count(1).count(*).count(列名) 区别 1. count(1).co ...
- java光标移动函数_文件内光标的移动 函数基础 定义函数的三种形式 函数的返回值 调用方式...
# with open(r'a.txt', 'r', encoding='utf-8')as f: # data1=f.read() # print('>1>:',data1) # pri ...
- SQL,三种排名函数,用作排名使用
前言: 在某些应用场景中,我们经常会遇到一些排名的问题,比如按成绩或年龄排名.排名也有多种排名方式,如直接排名.分组排名,排名有间隔或排名无间隔等等,这篇文章将总结几种MySQL中常见的排名问题. 创 ...
- 三种开窗函数详细用法,图文详解
开窗函数的详细用法 一,开窗函数的语法 二,从聚合开窗函数sum(score) over(partition by name )讲起 三,开窗函数之first_value,last_value,lea ...
- mybatis中LIKE模糊查询的几种写法以及注意点(亲测第三种CONCAT()函数的)
mybatis中对于使用like来进行模糊查询的几种方式: (1)使用${...} 注意:由于$是参数直接注入的,导致这种写法,大括号里面不能注明jdbcType,不然会报错 org.mybatis. ...
- STM32三种延时函数实现方法
想学习单片机的同学可以关注.私信我或者在评论区回复我要入门.在51入门的时候我们第一个实验就是点亮LED灯,如果没有延时,我们就很难看到亮灭效果. 1. STM32延时函数概述 在产品开发的过程中我们 ...
- 向函数传递结构的三种方式
向函数传递结构的三种方式 零.写在前面 在程序设计的过程当中,我们经常会涉及到选择数据的类型,在很多情况下,单单是普通的变量和数组并不足以满足我们的需求,这时候,C语言为我们提供了一种计较好用的数据类 ...
最新文章
- TPC性能测试及发布
- 大家都说 Java 反射效率低,为什么呢?
- redirect_uri参数错误解决方法
- OSS全球传输加速开启公测,助力企业业务全地域覆盖...
- centOS7 安装redis-3.2.6
- Ireport制作过程
- 【AI面试题】什么是数据不平衡,如何解决
- java登录验证技术,login_required -- 登录验证
- 你还会不会选择我(刘墉 )
- ubuntu MySQL数据库输入中文乱码 解决方案
- 微信小程序如何修改单页面背景色
- CountDownLatch--等待多线程计数器
- 建筑计算机辅助设计证书,学术讲座:计算机辅助设计绘图员(建筑类)职业技能鉴定...
- DataPipeline亮相“2021科技助力湾区数字金融发展峰会”,解锁“实时数据管理”密码
- IT业界新闻资讯网站推荐
- 怎么把aac转化为mp3,aac转mp3的3个方法
- Python学习之线性图
- C#使用Topshelf和Quartz开发处理定时任务的Windows服务程序
- RK3368 Recovery界面旋转方向调试
- 用Vue制作简易音乐播放器(超详细)