1.牛顿迭代
假设方程 在 附近有一个根,那么根据f(x)在x0附近的值和斜率,就能估计f(x)和x轴的交点用。迭代式子: 依次计算、、、……,那么序列将无限逼近方程的根。
用牛顿迭代法开平方】令: 所以f(x)的一次导是: 牛顿迭代式: 随便一个迭代的初始值,例如,代入上面的式子迭代。例如计算,即a=2。 …… 计算器上可给出
根据果壳文章以上阐述,由此我们可以写出如下代码:
求9的平方根,尝试 : 3次 , point : 3 , point的平方 : 9 , point平方与target的差值 : 0
到此为此,似乎问题已经得到了圆满的答案,但紧接着我就发现了更有趣的解法
2.魔法数开方倒数
暴雪是一家神奇的公司,它的代码总是能给人惊喜,例如 One-way Hash 被业内赞为最优秀的hash表改良算法(timer33算法应该才是最快的,One-way Hash 在Timer33的基础上改用两次Hash计算来比对Key是否一致,在数据结构没有根本变化的情况下,理论上来说应该更慢)。
下面是暴雪的开平方算法源码,它对上述的牛顿迭代做了一些改进:
这个算法的牛逼之处在于,经典的牛顿迭代里猜测值是随便取的一个值,而这段代码里却用了一个魔法数0x5f3759df来作为猜测值。这个算法只用了一次位运算,根本没有多次迭代就得到了结果,比直接用sqrt(n)快了大约四倍。
但是,我要说但是了,这个算法并不是暴雪原创的。它更早的时候出现在 雷神之锤3 这个游戏的3D引擎代码里,作者是约翰-卡马克(John Carmack),江湖人称卡神。
注意看代码
这个魔法数 0x5f3759df 。它是从哪来的,又起到了什么作用?
单精度的浮点数结构是这样:那么现在,每个正浮点数 y 可以用尾数和指数的形式写成![]()
开方的几种写法 ,其中 m 是尾数部分,取值范围是![]()
开方的几种写法 ;e 是指数部分,一个整数。每个浮点数所对应的「整数形式」则可以用![]()
开方的几种写法 表示,其中 L 是指数部分需要的位移次数(用 2 的幂表示),E 和 M 是指数部分和小数部分的整数版本。两者之间的关系是![]()
开方的几种写法 对单精度浮点而言,![]()
开方的几种写法 。 考虑对数![]()
开方的几种写法 ,由于![]()
开方的几种写法 ,![]()
开方的几种写法 ,取近似![]()
开方的几种写法 ,可以算出整体「偏差」最小的![]()
开方的几种写法 ,此时两者基本相当。因此我们可以说![]()
开方的几种写法 ……………… (1) 那么,对于 y 的整数形式 Y 而言,展开并带代入 (1) 有:![]()
开方的几种写法 即![]()
开方的几种写法 ………………(2) 那么对于![]()
开方的几种写法 来说,![]()
开方的几种写法 ,代入 (2) 得:![]()
开方的几种写法 解得![]()
开方的几种写法 ![]()
开方的几种写法
这个就是代码
的秘密所在。
程序员多学点数学,总是会有用的,与君共勉
附录:java版算法实现
引用文献: