社区活动专版
直播中

jf_1137202360

8年用户 1362经验值
擅长:嵌入式技术
私信 关注

【《计算》阅读体验】+徜徉于历史人物事件中-跑跑计算实例感叹于前人的智慧

本帖最后由 jf_1137202360 于 2024-6-28 13:59 编辑

本书的一大特点是从历史人物,历史事件中引出相关内容,并且引出大量的思考,有作者的思考也有抛出来引发读者的思考,引人入胜。
不知不觉带领读者也沉浸其中,徜徉在各种历史大牛人物,各种激动人心的历史事件之中,好像自己就是计算发展历史长河中的参与者。穿插着三次数学危机的介绍,让读者的心也跟着激动起来,甚至本人不由自主的想着,自己是否也能和那些历史大牛一样为数学的大厦出一份力。

抛开宏大的历史背景,事件等等,书中的一些有意思的例子也可以作为偶尔的调剂。作为程序员甚至就忍不住就要写个代码来实现下分享下。 从中也可以体验到古人的智慧。

这里就来分享一个计算平方根数的书中的例子,我们来写成代码测试下。
书中提到的美索不达米亚人计算正数的平方根的递归方法,
实际就是现在我们常用的牛顿迭代法,可见古人的智慧,虽然这个时候古人可能靠经验获取的这种方法,但是具体的理论建设很可能就是建立在这种经验启发之下,数学大厦也是这样不断慢慢形成的。

输出值=(输入值+x/输入值))/2,这是书中介绍的算法
实际我们现在可知道是牛顿迭代法

x的平方根值假设是ya的平方根实际就是找曲线x^(1/2)上值为a对应的x点。
换个思路把曲线整体下移a,即变为x^(1/2)-a,则是找新的曲线和x轴的交点,



可以先任意找一个点xk开始,找到其切线与x轴的交点即xk+1,此时计算y(k+1)0的误差还比较大则继续上述过程,以xk+1画切线找与x轴的交点即xk+2,直到y(k+n)接近于0满足误差要求则停止。

我们写出上述代码和现在编译器通常使用的算法(该算法使用的魔术数很有意思可以搜索一下其原理)

float my_sqrt(float a)
{
    float tmp = a/2;
    for(int i=0;i<10;i++)
    {
        tmp = (tmp + a/tmp)/2;
    }
    return tmp;
}

float MagicSqrt(float x)
{
    float xhalf = 0.5f * x;
    int i = *(int*)&x;
    i = 0x1fbd1df5 + (i >> 1);
    x = *(float*)&i;
    x = 0.5f * x + xhalf / x;

    return x;
}
    for(int i=1;i<10;i++)
    {
        printf("my_sqrt(%d):%f,srqt(%d):%f
",i,my_sqrt(i*1.0f),i,MagicSqrt(i*1.0f));
    }

结果如下,可见迭代的方法可以控制迭代次数获取更高的精度,但是相应的会慢一点,不得不感叹于古人的智慧。这也是为什么嵌入式开发中会有各种各样的实现,因为要满足不同 场景的需求,有些注重精度,有些注重速率,很难一种方法能满足所有需求。
my_sqrt(1):1.000000,srqt(1):1.000064
my_sqrt(2):1.414214,srqt(2):1.415568
my_sqrt(3):1.732051,srqt(3):1.732057
my_sqrt(4):2.000000,srqt(4):2.000128
my_sqrt(5):2.236068,srqt(5):2.236288
my_sqrt(6):2.449490,srqt(6):2.449496
my_sqrt(7):2.645751,srqt(7):2.646399
my_sqrt(8):2.828427,srqt(8):2.831136
my_sqrt(9):3.000000,srqt(9):3.001038


可能古人并不知道1/2是如何来的,现在我们知道其是x^(1/2)函数的导数的系数,即牛顿迭代,(泰勒展开的方法)。
实际上这个系数代表的是用直线(切线的斜率)去代表变化率,也就是大了就减小点,小了就增大点,这个增大减小的速率即这个导数系数,古人可能是通过经验获取的该值,后面我们才有了对应的理论。

不由的对应到我们现在,我们工程实践中很多常量都是经验值,也许只是我们还没有对应的建立理论体系了解根本原因而已,也许将来我们掌握了根本的原理时就会发现其实这些数都是可理论确定的。




更多回帖

发帖
×
20
完善资料,
赚取积分