不用sqrt()函数,求平方根的三种方法

2023-11-11

最近看到了这个比较有意思的题目,探究了一下。

当然有最暴力的方法,直接遍历,(0.0, x)区间内所有的数据(也可以是 x/2),看值是否相等,但该方法太过暴力,在此不讨论。

可以采用下面的 二分法牛顿法Quake III源码中的方法

浮点数的比较方法比较特别,不能使用平常的 x == 0的方法进行比较,下面都用到了equal比较方法,equal的代码:

const double Min_value = 1e-7; //定义最小的浮点数数误差,这里的用法比 宏更好
bool equal(double num1, double num2)
{
    if ((num1 - num2 > -Min_value) && (num1 - num2 < Min_value))
        return true;
    else
        return false;
}

1、二分法

类似于二分查找的思路,时间复杂度 O ( l o g n ) O(logn) O(logn)

double sqrt_binary_search(double square)
{
    if(square < Min_value)
        return -1;

    double low = 0.0;
    double high = square;

    // while (high - low > Min_value)
    while (!equal(high, low))
    {
        /* code */
        double mid = (high + low) / 2;
        if (mid * mid > square)
        {
            high = mid;
        }
        else
        {
            low = mid;
        }
    }

    return low;
}

2、牛顿法

先分析下牛顿法的原理,用的是牛顿迭代方法

牛顿迭代法又称为牛顿-拉弗森方法,实际上是由牛顿、拉弗森各自独立提出来的。牛顿-拉弗森方法提出来的思路就是利用切线是曲线的线性逼近这个思想。

随便找一个曲线上的A点(为什么随便找,根据切线是切点附近的曲线的近似,应该在根点附近找,但是很显然我们现在还不知道根点在哪里),做一个切线,切线的根(就是和x轴的交点)与曲线的根,还有一定的距离。牛顿、拉弗森们想,没关系,我们从这个切线的根出发,做一根垂线,和曲线相交于B点,继续重复刚才的工作:
在这里插入图片描述
经过多次迭代后会越来越接近曲线的根。

上面就是牛顿迭代的几何直觉。只有将上述过程用代数表示出来,才方便我们写代码。

  • 首先, x 0 x_0 x0处的切线方程为: y − y 0 = f ′ ( x ) ( x − x 0 ) y - y_0 = f'(x) (x - x_0) yy0=f(x)(xx0),即: f ( x ) = y = f ′ ( x ) ( x − x 0 ) + f ( x 0 ) f(x) = y = f'(x) (x - x_0) + f(x_0) f(x)=y=f(x)(xx0)+f(x0)

  • 要 求在 x n x_n xn所在切线方程交于x轴的点 x n + 1 x_{n+1} xn+1,即求解 0 = f ′ ( x ) ( x n + 1 − x n ) + f ( x n ) 0 = f'(x) (x_{n+1} - x_n) + f(x_n) 0=f(x)(xn+1xn)+f(xn),==> x n + 1 = x n − f ( x n ) f ′ ( x n ) x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)} xn+1=xnf(xn)f(xn)

  • 通过上面的公式可以得出: x n + 1 = x n − f ( x n ) f ′ ( x n ) x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)} xn+1=xnf(xn)f(xn),该公式可用于求任何次方的解。

  • 这里求的是 平方根,即求解 f ( x ) = x 2 = n f(x) = x^2 = n f(x)=x2=n时,x的值是多少。也就是求解: f ( x ) = x 2 − n = 0 f(x) = x^2 - n = 0 f(x)=x2n=0的解。

  • 由于 f ′ ( x ) = 2 x f'(x) = 2x f(x)=2x,所以 x n + 1 = x n − x n 2 − n 2 x n x_{n+1} = x_n - \frac{x_n^2 - n}{2x_n} xn+1=xn2xnxn2n,即 x n + 1 = x n + n x n 2 x_{n+1} = \frac{x_n + \frac{ n}{x_n}}{2} xn+1=2xn+xnn

实现上诉公式时,一个小改进:计算机不善于做除法,所以这里修改为乘 0.5。

double sqrt_newton(double square)
{
    if (square < Min_value)
        return -1;
        
    double ret = 1.0;
    while (!equal(ret*ret, square))
    {
        ret = (ret + square / ret) * 0.5;
    }

    return ret;
}

上面的初始值是随意选择的,选择的好坏一定程度上影响了计算的速度。如,下面的方法选择的初始值是 0x5f3759df

3、来自于Quake III源码的解法

double InvSqrt(double x)
{
    double xhalf = 0.5f * x;
    int i = *(int *)&x;             // get bits for floating value
    i = 0x5f3759df - (i >> 1);      // gives initial guess y0
    x = *(double *)&i;              // convert bits back to float
    x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy
    return x;
}

4、完整代码

#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <cmath>

using namespace std;

// const double Min_value = 0.0000001;
const double Min_value = 1e-7;

bool equal(double num1, double num2)
{
    if ((num1 - num2 > -Min_value) && (num1 - num2 < Min_value))
        return true;
    else
        return false;
}

double sqrt_binary_search(double square)
{
    if(square < Min_value)
        return -1;

    double low = 0.0;
    double high = square;

    // while (high - low > Min_value)
    while (!equal(high, low))
    {
        /* code */
        double mid = (high + low) / 2;
        if (mid * mid > square)
        {
            high = mid;
        }
        else
        {
            low = mid;
        }
    }

    return low;
}

double sqrt_newton(double square)
{
    if (square < Min_value)
        return -1;
        
    // double ret = 0x5f3759df; // 另一种初值
    double ret = 1.0;
    while (!equal(ret*ret, square))
    {
        ret = (ret + square / ret) * 0.5;
    }

    return ret;
}

double InvSqrt(double x)
{
    double xhalf = 0.5f * x;
    int i = *(int *)&x;             // get bits for floating value
    i = 0x5f3759df - (i >> 1);      // gives initial guess y0
    x = *(double *)&i;              // convert bits back to float
    x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy
    return x;
}

int main()
{
    double x;
    scanf("%lf", &x);

    int start = clock();
    printf("sqrt_binary_search:%lf sqrt = %lf, time = %lf\n", x, 
    		sqrt_binary_search(x), (double)(clock() - start) / CLOCKS_PER_SEC);

    start = clock();
    printf("sqrt_newton:%lf sqrt = %lf, time = %lf\n", x, sqrt_newton(x), 
    		(double)(clock() - start) / CLOCKS_PER_SEC);

    start = clock();
    printf("InvSqrt:%lf sqrt = %lf, time = %lf\n", x, InvSqrt(x), 
    		(double)(clock() - start) / CLOCKS_PER_SEC);

    start = clock();
    printf("sqrt:%lf sqrt = %lf, time = %lf\n", x, sqrt(x), 
    		(double)(clock() - start) / CLOCKS_PER_SEC);
}

上述代码经过测试,可以发现 来自于Quake III源码的解法 和 cmath中的 sqrt 的速度差不多。

在这里插入图片描述

参考

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

不用sqrt()函数,求平方根的三种方法 的相关文章

随机推荐

  • iOS推送(利用极光推送)

    本文主要是基于极光推送的SDK封装的一个快速集成极光推送的类的封装 不喜勿喷 1 首先说一下推送的一些原理 Push的原理 Push 的工作机制可以简单的概括为下图图中 Provider是指某个iPhone软件的Push服务器 这篇文章我将
  • Linux SSH登录服务器报ECDSA host key “ip地址“ for has changed and you have requested strict checking错误

    Linux SSH命令用了那么久 第一次遇到这样的错误 ECDSA host key ip地址 for has changed and you have requested strict checking 记录下方便记忆 解决方案 在终端上
  • 系统没有wmi服务器,系统没有WMI服务怎么办.WMI错误修复方法

    WMI是一项核心的Windows管理技术 WMI作为一种规范和基础结构 通过它可以访问 配置 管理和监视几乎所有的Windows资源 比如用户可以在远程计算机器上启动一个进程 设定一个在特定日期和时间运行的进程 远程启动计算机 获得本地或远
  • XCTF_Web_新手练习区:simple_js(源代码详解)

    文章目录 第七题 simple js 源代码详解 目标 Writeup 源代码详解 第七题 simple js 源代码详解 目标 掌握有关js的知识 Writeup 进入环境后我们遇到了输入密码 于是我们随便输入一个密码 点击确定 之后啥也
  • Android应用底部导航栏(选项卡)实例

    现在很多android的应用都采用底部导航栏的功能 这样可以使得用户在使用过程中随意切换不同的页面 现在我采用TabHost组件来自定义一个底部的导航栏的功能 我们先看下该demo实例的框架图 其中各个类的作用以及资源文件就不详细解释了 还
  • 十分钟利用windows7漏洞破解开机密码

    所有win7系统都使用 首先连按五下Shift键弹出粘滞键提醒 然后我们点击否后关机 启动系统时将其强制关机 虚拟机利用电源关闭虚拟机 自用主机就在开机时长按关机键强制关闭系统 随后启动系统 我们选择启动启动修复 推荐 选择取消即不还原 等
  • Python数据可视化——折线图

    Python数据可视化 折线图 随着数据分析和数据科学的飞速发展 数据可视化成为了越来越重要的一环 而Python作为一门强大的编程语言 其在数据可视化领域也有着不俗的表现 本文将为大家介绍如何使用Python的Matplotlib库创建一
  • 【Transformers】第 6 章:用于标记分类的微调语言模型

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • Vue.config.js常用配置详解

    摘要 本文将介绍Vue config js中常用的配置选项 包括publicPath outputDir devServer chainWebpack等 并提供相应的代码示例 帮助读者更好地理解和配置Vue项目 1 publicPath p
  • 新汽车电子技术图谱

    商业模式 改变传统对于OEM来讲的 卖车即结束 的模式 会员模式 共享模式 租赁模式 运营模式等各种新型的数字出行体验模式 OTA云 远程刷新 远程诊断 远程车控 远程数据上传 第三方App 应用商店 边缘计算 多级云计算 大数据处理 AI
  • Android4.4深入浅出之SurfaceFlinger与Client通信框架(一)

    SurfaceFlinger框架是基于Binder进程间通信机制搭建的 SF作为一个服务进程 用户程序想要跟它通信必然要经过Binder机制 首先说一下 用户要跟SF通信 那么SF必须出现在ServiceManager中 因为SF也是一个服
  • ROS STAGE教程1

    默认路径opt ros kinetic share 下有stage 和 stage ros 到该路径下可运行 rosrun stage ros stageros rospack find stage ros world willow err
  • STM32+HC-05蓝牙模块学习与使用

    HC 05蓝牙串口通信 HC05模块是一款高性能主从一体蓝牙串口模块 是一种集成蓝牙功能的PCBA板 用于短距离无线通信 十分方便 从某宝商家那里可以看到 蓝牙可以使用多种方法使用 这里我使用的是蓝牙主机连接 所以我们这里需要准备的器件 两
  • 【python学习】函数式编程和高阶函数 map filter reduce lambda表达式 sorted 闭包 装饰器

    函数式编程就是一种抽象程度很高的编程范式 纯粹的函数式编程语言编写的函数没有变量 因此 任意一个函数 只要输入是确定的 输出就是确定的 这种纯函数我们称之为没有副作用 而允许使用变量的程序设计语言 由于函数内部的变量状态不确定 同样的输入
  • cudaMemcpy() 犯错误

    cudaMemcpy void dst const void src size t count enum cudaMemcpyKind kind 错误 count 是 bytes 个数 不是数据类型个数 让我debug好久的一个错误啊 转载
  • YUV图像数据分析

    做视频采集与处理 自然少不了要学会分析YUV数据 因为从采集的角度来说 一般的视频采集芯片输出的码流一般都是YUV数据流的形式 而从视频处理 例如H 264 MPEG视频编解码 的角度来说 也是在原始YUV码流进行编码和解析 所以 了解如何
  • rust异步编程2

    概述 异步编程参考书籍 async book 此学习根据Rust语言圣经 中tokio专栏 tokio 是一个将 rust提供的async await 特性编写的异步代码运行起来的异步运行时 tokio async std smol等异步运
  • uniapp css

    ifdef APP PLUS height calc var status bar height 80upx endif 计算 状态栏 其他高度
  • 信息学奥赛一本通:2073:【例2.16 】三角形面积

    题目描述 传说古代的叙拉古国王海伦二世发现的公式 利用三角形的三条边长来求取三角形面积 已知 ABC中的三边长分别为a b c 求 ABC的面积 提示 海伦公式 s p p a p b p c 其中p a b c 2 输入 三角形的三条边长
  • 不用sqrt()函数,求平方根的三种方法

    最近看到了这个比较有意思的题目 探究了一下 文章目录 1 二分法 2 牛顿法 3 来自于Quake III源码的解法 4 完整代码 参考 当然有最暴力的方法 直接遍历 0 0 x 区间内所有的数据 也可以是 x 2 看值是否相等 但该方法太