0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

带大家领略sqrt的神奇之处:开平方的7种算法介绍

算法与数据结构 来源:未知 2019-11-28 09:29 次阅读

作者:nash_

sqrt()函数,是绝大部分语言支持的常用函数,它实现的是开方运算;开方运算最早是在我国魏晋时数学家刘徽所著的《九章算术》被提及。今天写了几个函数加上国外大神的几个神级程序带大家领略sqrt的神奇之处。

1、古人算法(暴力法)

原理:从0开始0.00001,000002...一个一个试,直到找到x的平方根,代码如下:

publicclassAPIsqrt{

staticdoublebaoliSqrt(doublex){

finaldouble_JINGDU=1e-6;
doublei;
for(i=0;Math.abs(x-i*i)>_JINGDU;i+=_JINGDU)
;
returni;
}

publicstaticvoidmain(String[]args){
doublex=3;
doubleroot=baoliSqrt(x);
System.out.println(root);
}

测试结果:

1、7320509999476947

2、牛顿迭代法

计算机科班出身的童鞋可能首先会想到的是《数值分析》中的牛顿迭代法求平方根。原理是:随意选一个数比如说8,要求根号3,我们可以这么算:

(8 + 3/8) = 4.1875

(4.1875 + 3/4.1875) = 2.4519

(2.4519 + 3/2.4519) = 1.837

(1.837 + 3/1.837) = 1.735

做了4步基本算出了近似值了,这种迭代的方式就是传说中的牛顿迭代法了,代码如下:

publicclassAPIsqrt{

staticdoublenewtonSqrt(doublex){

if(x< 0){
System.out.println("负数没事开什么方");
return-1;
}
if(x==0)
return0;

double_avg=x;
doublelast_avg=Double.MAX_VALUE;
finaldouble_JINGDU=1e-6;

while(Math.abs(_avg-last_avg)>_JINGDU){
last_avg=_avg;
_avg=(_avg+x/_avg)/2;
}
return_avg;
}

publicstaticvoidmain(String[]args){
doublex=3;
doubleroot=newtonSqrt(x);
System.out.println(root);
}
}

测试结果:

17320508075688772

3、暴力-牛顿综合法

原理:还是以根号3为例,先用暴力法讲根号3逼近到1.7,然后再利用上述的牛顿迭代法。虽然没有用牛顿迭代好,但是也为我们提供一种思路。代码如下:

publicclassAPIsqrt{

staticdoublebaoliAndNewTonSqrt(doublex){

if(x< 0){
System.out.println("负数没事开什么方");
return-1;
}
if(x==0)
return0;

doublei=0;
double_avg;
doublelast_avg=Double.MAX_VALUE;
for(i=0;i*i< x; i += 0.1);
_avg=i;

finaldouble_JINGDU=1e-6;

while(Math.abs(_avg-last_avg)>_JINGDU){
last_avg=_avg;
_avg=(_avg+x/_avg)/2;
}
return_avg;
}

publicstaticvoidmain(String[]args){
doublex=3;
doubleroot=baoliAndNewTonSqrt(x);
System.out.println(root);
}
}

测试结果:

1、7320508075689423

4、二分开方法

原理:还是以3举例:

(0+3)/2 = 1.5, 1.5^2 = 2.25, 2.25 < 3;

(1.5+3)/2 = 2.25, 2.25^2 = 5.0625, 5.0625 > 3;

(1.5+2.25)/2 = 1.875, 1.875^2 = 3.515625; 3.515625>3;

直到前后两次平均值只差小于自定义精度为止,代码如下:

publicclassAPIsqrt{

staticdoubleerfenSqrt(doublex){

if(x< 0){
System.out.println("负数没事开什么方");
return-1;
}
if(x==0)
return0;

finaldouble_JINGDU=1e-6;
double_low=0;
double_high=x;
double_mid=Double.MAX_VALUE;
doublelast_mid=Double.MIN_VALUE;

while(Math.abs(_mid-last_mid)>_JINGDU){

last_mid=_mid;
_mid=(_low+_high)/2;
if(_mid*_mid>x)
_high=_mid;
if(_mid*_mid< x)
                _low = _mid;

        }
        return_mid;

}

publicstaticvoidmain(String[]args){
doublex=3;
doubleroot=erfenSqrt(x);
System.out.println(root);
}
}

测试结果:

1、732051134109497

5、计算 (int)(sqrt(x))算法

PS:此算法非博主所写

原理:空间换时间,细节请大家自行探究,代码如下:

publicclassAPIsqrt2{
finalstaticint[]table={0,16,22,27,32,35,39,42,45,48,50,53,
55,57,59,61,64,65,67,69,71,73,75,76,78,80,81,83,84,
86,87,89,90,91,93,94,96,97,98,99,101,102,103,104,
106,107,108,109,110,112,113,114,115,116,117,118,119,
120,121,122,123,124,125,126,128,128,129,130,131,132,
133,134,135,136,137,138,139,140,141,142,143,144,144,
145,146,147,148,149,150,150,151,152,153,154,155,155,
156,157,158,159,160,160,161,162,163,163,164,165,166,
167,167,168,169,170,170,171,172,173,173,174,175,176,
176,177,178,178,179,180,181,181,182,183,183,184,185,
185,186,187,187,188,189,189,190,191,192,192,193,193,
194,195,195,196,197,197,198,199,199,200,201,201,202,
203,203,204,204,205,206,206,207,208,208,209,209,210,
211,211,212,212,213,214,214,215,215,216,217,217,218,
218,219,219,220,221,221,222,222,223,224,224,225,225,
226,226,227,227,228,229,229,230,230,231,231,232,232,
233,234,234,235,235,236,236,237,237,238,238,239,240,
240,241,241,242,242,243,243,244,244,245,245,246,246,
247,247,248,248,249,249,250,250,251,251,252,252,253,
253,254,254,255};

/**
*Afasterreplacementfor(int)(java.lang.Math.sqrt(x)).Completely
*accurateforx< 2147483648(i.e.2^31)...
*/
staticintsqrt(intx){
intxn;

if(x>=0x10000){
if(x>=0x1000000){
if(x>=0x10000000){
if(x>=0x40000000){
xn=table[x>>24]<< 8;
}else{
xn=table[x>>22]<< 7;
}
}else{
if(x>=0x4000000){
xn=table[x>>20]<< 6;
}else{
xn=table[x>>18]<< 5;
}
}

xn=(xn+1+(x/xn))>>1;
xn=(xn+1+(x/xn))>>1;
return((xn*xn)>x)?--xn:xn;
}else{
if(x>=0x100000){
if(x>=0x400000){
xn=table[x>>16]<< 4;
}else{
xn=table[x>>14]<< 3;
}
}else{
if(x>=0x40000){
xn=table[x>>12]<< 2;
}else{
xn=table[x>>10]<< 1;
}
}

xn=(xn+1+(x/xn))>>1;

return((xn*xn)>x)?--xn:xn;
}
}else{
if(x>=0x100){
if(x>=0x1000){
if(x>=0x4000){
xn=(table[x>>8])+1;
}else{
xn=(table[x>>6]>>1)+1;
}
}else{
if(x>=0x400){
xn=(table[x>>4]>>2)+1;
}else{
xn=(table[x>>2]>>3)+1;
}
}

return((xn*xn)>x)?--xn:xn;
}else{
if(x>=0){
returntable[x]>>4;
}
}
}

return-1;
}
publicstaticvoidmain(String[]args){
System.out.println(sqrt(65));

}
}

测试结果:8

6、最快的sqrt算法

PS:此算法非博主所写

这个算法很有名,大家可能也见过,作者是开发游戏的,图形算法中经常用到sqrt,作者才写了一个神级算法,和他那神秘的0x5f3759df,代码如下

#include
floatInvSqrt(floatx)
{
floatxhalf=0.5f*x;
inti=*(int*)&x;//getbitsforfloatingVALUE
i=0x5f375a86-(i>>1);//givesinitialguessy0
x=*(float*)&i;//convertbitsBACKtofloat
x=x*(1.5f-xhalf*x*x);//Newtonstep,repeatingincreasesaccuracy
returnx;
}

intmain()
{
printf("%lf",1/InvSqrt(3));

return0;
}

测试结果:

感兴趣的朋友可以参考http://wenku.baidu.com/view/a0174fa20029bd64783e2cc0.html 是作者解释这个算法的14页论文《Fast Inverse Square Root》

7、一个与算法6相似的算法

PS:此算法非博主所写

代码如下:

#include
floatSquareRootFloat(floatnumber){
longi;
floatx,y;
constfloatf=1.5F;

x=number*0.5F;
y=number;
i=*(long*)&y;
i=0x5f3759df-(i>>1);
y=*(float*)&i;
y=y*(f-(x*y*y));
y=y*(f-(x*y*y));
returnnumber*y;
}

intmain()
{
printf("%f",SquareRootFloat(3));

return0;
}

测试结果:


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4455

    浏览量

    90756
  • 函数
    +关注

    关注

    3

    文章

    3868

    浏览量

    61309

原文标题:开平方的 7 种算法

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    单片机开平方的快速算法

    本帖最后由 scan0123 于 2012-8-13 14:52 编辑 单片机开平方的快速算法为工作的需要,要在单片机上实现开根号的操作。目前开平方的方法大部分是用牛顿迭代法。我在查了一些资料
    发表于 08-02 14:40

    VeriLog 开平方怎么做?

    VeriLog 开平方怎么做?
    发表于 10-11 14:09

    关于开平方算法

    电机算法中会用到开平方算法,是个相对复杂和耗时些的数学算法C math库的double sqrt耗时太长,不能用我没有用过IQmath库,自
    发表于 09-27 11:27

    程序中定义了一个变量进行sqrt()开根号处理与预想的结果不对

    我在程序中定义了一个变量对其进行sqrt()开根号处理,结果与预想的不对。在TMS320F28335里面,执行开平方有什么注意的吗?
    发表于 11-05 11:45

    图像算法有什么神奇之处

    吴军在《数学之美》中,用通俗易懂的语言,让非专业读者领略了一把数学的魅力。数学作为自然科学的基础,它的应用遍及物理、工程、生物、化学和经济等多个领域,在拍照这样一个似乎不太相关的领域,数学算法再一次
    发表于 09-17 09:05

    STC12C5A60S2无法实现开平方算法怎么回事

    求助!STC12C5A60S2无法实现开平方算法sqrt函数),以及atan2和asin怎么办?我已经包含了相关的头文件了,但是编译通不过。
    发表于 05-20 09:07

    在单片机中开平方会用到哪些算法

    C语言中开平方算法中要开平方的话,可以在头文件中加#include .然后调sqrt(n);函数即可.但在单片机中要开平方.可以用到下面
    发表于 07-15 07:03

    基于定点DSP的浮点开平方算法的实现

    本文提出了基于TMS320C2XX 定点DSP 的浮点开平方算法,给出了实现方法及程序清单。实践证明该方法具有精度高、运算速度快、程序简单等特点。以美国 TI 公司的TMS320C2XX 为代
    发表于 07-31 08:11 42次下载

    基于逐次逼近的VHDL开平方算法

    Cyclone 器件为在FPGA 上实现低成本的数字信号处理(DSP)系统提供了一个理想的平台,成为大规模生产时的最佳方案.。本文介绍了在以EP1C6 芯片为核心的硬件系统中,运用VHDL 实现
    发表于 11-30 14:14 32次下载

    MCS-51单片机实用子程序库实验(六)

    标号: FSQR 功能:浮点数开平方(快速逼近算法)入口条件:操作数在[R0]中。出口信息:OV=0时,平方根仍在[R0]中,OV=1时,负数开平方
    发表于 01-08 10:11 54次下载

    电源变压器设计与计算铁心面积公式

    电源变压器设计与计算铁心面积公式:铁心面积=容量开平方;以100W为例;100开平方=10平方厘米.电子管输出变压器的铁心面积=容量开平方*2.以15W输出变压器为例:15
    发表于 10-23 14:39 7056次阅读
    电源变压器设计与计算铁心面积公式

    电力系统微机保护中开平方运算的一种新的快速算法

    电力系统微机保护中开平方运算的一种新的快速算法
    发表于 11-02 11:03 8次下载

    单片机快速开平方算法资料和代码免费下载

    C语言中开平方算法中要开平方的话,可以在头文件中加#include 。然后调sqrt(n);函数即可。但在单片机中要开平方。可以用到下面
    发表于 09-24 17:18 1次下载
    单片机快速<b class='flag-5'>开平方</b>的<b class='flag-5'>算法</b>资料和代码免费下载

    单片机快速开平方算法

    C语言中开平方算法中要开平方的话,可以在头文件中加#include .然后调sqrt(n);函数即可.但在单片机中要开平方
    发表于 11-11 13:36 1次下载
    单片机快速<b class='flag-5'>开平方</b>的<b class='flag-5'>算法</b>

    NI Multisim 10经典教程分享--除法与开平方运算电路

    NI Multisim 10经典教程分享--除法与开平方运算电路
    的头像 发表于 02-08 09:18 1308次阅读