惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

TaoSecurity Blog
TaoSecurity Blog
L
LINUX DO - 最新话题
Help Net Security
Help Net Security
N
News | PayPal Newsroom
www.infosecurity-magazine.com
www.infosecurity-magazine.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
The Last Watchdog
The Last Watchdog
S
Security @ Cisco Blogs
W
WeLiveSecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
Webroot Blog
Webroot Blog
T
Troy Hunt's Blog
V
Vulnerabilities – Threatpost
Google Online Security Blog
Google Online Security Blog
N
News and Events Feed by Topic
T
Threat Research - Cisco Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tor Project blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Darknet – Hacking Tools, Hacker News & Cyber Security
PCI Perspectives
PCI Perspectives
Google DeepMind News
Google DeepMind News
T
Tailwind CSS Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
S
SegmentFault 最新的问题
J
Java Code Geeks
P
Privacy & Cybersecurity Law Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 【当耐特】
博客园_首页
H
Hacker News: Front Page
T
Threatpost
Jina AI
Jina AI
博客园 - Franky
月光博客
月光博客
L
LINUX DO - 热门话题
The Cloudflare Blog
H
Heimdal Security Blog
博客园 - 司徒正美
酷 壳 – CoolShell
酷 壳 – CoolShell
Cloudbric
Cloudbric
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
S
Secure Thoughts
T
Tenable Blog
I
Intezer
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻

Long Luo's Life Notes

夏至日测地球:利用太阳影子计算地球半径 太阳温度是怎么计算出来的? 《大象的时间,老鼠的时间》读书笔记:生命节奏背后的数学规律 小港流到哪里去? 如何用一根棍子测出地球有多大?复刻埃拉托色尼的春分实验 2007江苏高考数学第20题解析:一道通向黄金分割数的数列压轴题 Google经典面试题: 鸡蛋应该怎么扔? 2010年江苏高考数学压轴题解析:巧用余弦定理与数学归纳法 2011年清华大学自主招生数学题解析:一道经典数列题的解法与思路 2011年清华大学自主招生数学题解析:一道经典数列题的解法与思路 2006年江西高考理科数学压轴题解析:递推、放缩与不等式结构 2006年江西高考理科数学压轴题解析:递推、放缩与不等式结构 一道初中数学极值题的多种解法:柯西不等式、几何法、函数法详解 扔几个骰子,怎么算出期望?——拼多多校招笔试算法题的数学故事 拼多多校招笔试算法题:一行公式搞定“多多的魔术盒子” 斯特林公式(Stirling's Formula):我一个阶乘表达式,怎么就和圆扯上关系了呢? 我爱做题:2010年江西高考理科数学压轴题 热机的效率上限在哪里?解析卡诺循环(Carnot Cycle) 为什么 2024 年会有 366 天? 数学之美:几何视角下的高斯积分(Gaussian Integral) 从最小二乘法到正态分布:高斯是如何找到失踪的谷神星的? 正态分布(Normal Distribution)公式为什么长这样? 高速公路编号背后的数学密码 2024阿里巴巴全球数学竞赛预选赛试题及解答 库函数 (libm) 是如何计算三角函数值的? payne hanek 归约算法 音乐背后的数学 素描背后的物理 cody waite 浮点数 Remez Algorithm 素描背后的数学 发生在计算机内存里的进化:解密遗传算法(Genetic Algorithm) CORDIC算法:一种高效计算三角函数值的方法 墨卡托的魔术:地图是如何欺骗你的眼睛的? PID 算法到底在干什么?工程师最常用的控制方法 解密卡尔曼滤波(Kalman Filter)算法:深入解析卡尔曼滤波算法原理与在线可视化实例 从记忆到洞察:轻松掌握泰勒展开式(Taylor Series)的记忆技巧 哪个更大呢? $2^{100!}$ 还是 $2^{100}!$ ? Google经典编程竞赛题:计算 $(3 + \sqrt{5})^n$ 的小数点前三位数 手写数字识别:解码机器学习的背后的数学原理 The Answers of MRI Tutorial Videos gdb 操作指南 Linux 网络命令指南 贝塞尔曲线(Bezier Curve):优雅背后的数学原理 LeetCode 380. Insert Delete GetRandom O(1) Data Structures: Thought Process from HashMap to HashMap + Array LeetCode 2475. 数组中不等三元组的数目 2种 O(n) 时间复杂度算法 LeetCode 947. Most Stones Removed with Same Row or Column It is Literally a Graph: DFS and Union Find LeetCode 295. Find Median from Data Stream Two Heaps with the Follow Ups LeetCode 295. Find Median from Data Stream Two Heaps with the Follow Ups LeetCode 1668. 最大重复子字符串 不用API,比KMP更易理解简洁优雅的暴力解法 LeetCode 334. Increasing Triplet Subsequence Why Greedy Works? LeetCode 迷宫问题(The Maze)
参数归约算法(Argument Range Reduction):如何在浮点数环境下计算超大数字的三角函数值?
2023-09-16 · via Long Luo's Life Notes

By Long Luo

之前写过一篇介绍 CORDIC 算法 1 的文章,里面提到 CORDIC 算法的 不足 之处,CORDIC 算法的输入角度范围需要在 \([−99.88^{\circ} , 99.88^{\circ}]\) ,那么我们不禁要问,如果输入角度 \(\large {\theta }\) 很大的话,怎么处理呢?

这个问题同样存在于 泰勒展开式(Taylor series) 2 中,比如 \(\large {\sin (x) }\)\(\large {\cos (x) }\) 的泰勒展开式:

\[ \sin(x) = x - \frac {1}{3!}x^3 + \frac {1}{5!}x^5 - \frac {1}{7!} x^7 + \frac {1}{9!} x^9 + o(x^9) \quad \forall x \subset \mathbb{R} \]

\[ \cos(x) = 1 - \frac {1}{2!}x^2 + \frac {1}{4!}x^4 - \frac {1}{6!} x^6 + \frac {1}{8!} x^8 + o(x^8) \quad \forall x \subset \mathbb{R} \]

虽然在整个实数集 \(\large { \mathbb{R}}\) 都成立,但是在实际应用中因为展开项数限制和浮点数的精度限制, \(\large {x}\) 的范围只有在接近 \(\large {0}\) 的时候才有比较高的精度。

但是实际应用中,如果输入 \(\large {x}\) 很大的话,比如 \(\large {2^{32}, 10^{10}, 10^{22} \dots }\) 情况下怎么得到足够精确的值呢?

中学里我们知道三角函数是周期函数,对于比较大的值,我们可以使用下面的公式将值归约到一个比较小的范围内。

\[ x' = x - 2k \pi \quad k \subset \mathbb{Z} \]

这就是我们今天要讲的 参数归约(Argument Reduction) 算法。

从小学计算题开始

参数归约 听起来就很唬人,什么是参数啊,什么归约啊,都是些高大上的名词,听起来云里雾里的!

为了不让大家产生厌倦和畏难心理,我们先从一道小学数学计算题开始:

不借助计算器,计算 \(\large {66600 \times 666000}\) 的值!

对于这道题,大家可能会列出下列算术:

\[ 66600 \times 666000 = 666 \times 666 \times 100000 = 44355600000 \]

但其实呢,我们也可以使用下面的方法:

\[ \begin{aligned} 66600 \times 666000 &= 111^2 \times 4 \times 9 \times 10^5 \\&= 444 \times 999 \times 10^5 \\&= 444 \times (1000 - 1) \times 10^5 \\&= 4443556 \times 10^5 \end{aligned} \]

如果我说上面这 \(\large {2}\) 种方法都用到了参数归约的思想,你可能会感到震惊,什么?这种小学计算题也用到了参数归约算法吗?

上一章计算 \(\large {66600 \times 666000}\) 时,我们将 \(\large {666 \times 666}\) 化简为 \(\large {444 \times (1000 - 1)}\) ,再在结果后面直接加上 \(\large {5}\)\(\large {0}\) ,那么你有没有想过这背后隐含了什么数学思想吗?

下面我们正式进入今天的课题:参数归约(Argument Reduction)

为了提高数学函数的计算效率,将初始问题转变或者说缩小到函数更容易计算的域内,这就是参数归约。

已知函数 \(\large {f}\) ,求 \(\large {y = f(x)}\) 的值,可以通过以下 \(\large {3}\) 个步骤进行计算:

  1. \(\large {x}\) 转换为缩小的参数 \(\large {x'}\)
  2. 计算 \(\large {y' = f(x')}\)
  3. 使用函数恒等式从 \(\large {f(x')}\) 计算出 \(\large {f(x)}\)

现在回到上一节的小学数学计算题,我们实际上用到了 \(\large {2}\) 种参数归约:

  1. 指数/对数 运算公式

\[ exp(x + y) = \exp(x) \exp(y) \]

\[ \log (xy) = \log (x) + \log (y) \]

  1. 相加公式。不过上面小学数学题用的非常简单的分配律和结合律,实际上我们用的更复杂的公式,比如各种三角恒等式:

\[ \sin (x + y) = \sin(x) \cos (y) + \cos (x) \sin (y) \]

\[ \tan (x + y) = \frac {\tan (x) + \tan (y)}{1 - \tan (x) \tan (y)} \]

实际上为了让幂级数更快地收敛,通常我们取 \(\large {x = y}\) 以获得双倍公式,比如 \(\large {e^ {2x} = (e^x)^2}\) ,比如 快速幂算法(Exponentiation by squaring) 3 , 其具体实现可参考这篇文章: Fast Power Algorithm: Binary Exponentiation

而计算器中也常用到三倍角公式 \(\large {\sin (3x) = 3 \sin (x) - 4 \sin ^3(x)}\) 去计算三角函数值,具体可参考这个视频: 计算器是如何计算出三角函数和对数的?

可能有同学会问,那二倍角公式 \(\large {\sin (2x) = 2 \sin(x) \cos (x)}\) 就不用了吗?这个谜底待后续章节介绍。

如何对参数进行归约?

这一章我们来讲如何进行参数归约,通常我们区分 \(\large {2}\) 种参数归约:

  1. 加法参数归约: \(\large {x' = x - kC}\) ,其中 \(\large {C}\) 是实常数, \(\large {k}\) 是整数。

这种归约可以应用在 \(\large {f(x)}\) 是周期函数的情况,比如三角函数,此时 \(\large {C = 2 \pi}\) ;也可以应用于其他函数,比如小学数学我们知道计算 \(\large { \frac {a}{b}}\) 就是看有多少个 \(\large {b}\) 相加小于等于 \(\large {a}\) ,具体可参考这篇文章:29. Divide Two Integers

  1. 乘法参数归约:\(\large {x' = \frac{x}{kC}}\),其中 \(\large {C}\) 是实常数, \(\large {k}\) 是整数。

应用于计算指数函数 \(\large { \exp(x)}\) 时,其中 \(\large {C = 2}\)

值得注意的是,对于给定的函数,两种参数归约方式都可能使用。例如,对于 \(\large {\sin (x) }\) ,我们既可以使用三倍角公式 \(\large {\sin (3x) = 3 \sin (x) - 4 \sin^3 (x)}\) 化简,也可以使用加法归约 \(\large {\sin (x + 2 k \pi) = \sin (x)}\)

数值分析 Numerical Analysis

通过上面的分析,现在让我们去计算任意输入 \(\large {x}\)\(\large { \sin (x)}\)\(\large {\cos (x)}\) 的值,可以分为下面 \(\large {2}\) 种情况:

  1. \(\large {0 < x \leq \frac {\pi}{2}}\) ,使用泰勒展开或者 CORDIC 算法;
  2. \(\large {x > \frac {\pi}{2}}\) ,先将 \(\large {x}\) 归约到 \(\large {x' = x + k \frac {\pi}{2}}\) ,再回到第一步计算。

听起来似乎很简单,但事实上远远没有这么容易!

我们的电脑是基于 二进制(Binary) 4 的,本质只是高电平和低电平在电路上切换运行而已。因为 CPU 种的 逻辑运算单元(Arithmetic logic unit) 5 只能做加法和移位操作,因此而诞生了 计算机算术(Computer Arithmetic) 6 这门学科!

数学中有一门学科 数值分析(Numerical Analysis) 7 就是专门研究各种计算的!

虽然三角函数的周棋是 \(\large {2 \pi}\) ,但实际上我们只用归约到 \(\large {[-\frac {\pi}{4},\frac {\pi}{4}]}\) 即可,这里大家可以想想为什么?

之前我以为数值运算对于 \(\large {[-\frac {\pi}{2},\frac {\pi}{2}]}\) 的参数,会使用 CORDIC 算法,但实际上我看了一些数值计算库,发现对于 \(\large {[-\frac {\pi}{2},\frac {\pi}{2}]}\) 还是使用泰勒(Taylor Series) 8 逼近,当然里面用了很多技巧,大家可以看看库函数的具体实现即可解惑(这里先挖个坑,等我彻底看懂了再来这里填坑!)。

那对于 \(\large {x > \frac {\pi}{2}}\) ,如何计算呢?

Cody-Waite 归约算法

我们可以使用下列公式将 \(\large {x}\) 归约到 \(\large {[-\frac {\pi}{4},\frac {\pi}{4}]}\)

\[ x' = x - \lfloor \frac {x}{\frac {\pi}{2}} \rfloor \times \frac {\pi}{2} \]

我们可以很容易按照上述思想写出对应的代码,这就是 Cody & Waite 提出的 Cody-Waite 归约算法9

但是如果你认为这样就高枕无忧了的话,就太早了!

假如输入 \(\large {x = 1000001}\) 的话,上面的方法就会失效!想一想为什么?

Payne-Hanek 归约算法

上一章提出了一个问题,有效数字 超过 \(\large {15}\) 位的超大数字该如何计算呢?针对这个问题, Payne 与 Hanek 10 提出把浮点运算转换为大整数运算,来解决超大数字的浮点数归约问题。

要弄懂 Payne-Hanek 归约算法,需要对数学有比较深的理解,下面一步一步来分析!

对于输入 \(\large {x}\)

\[ x = k \cdot (\frac {\pi}{2}) + r \quad k \subset \mathbb{Z}, r \subset [-\frac {\pi}{4},\frac {\pi}{4}] \]

两边同乘 \(\large {\frac{2}{\pi }}\) ,可得:

\[ x \cdot ( \frac{2}{\pi }) = k + r \cdot (\frac{2}{\pi }) \]

因为 \(\left| r \right | \leq \frac {\pi}{4}\)\(r \cdot (\frac {2}{\pi }) \leq 0.5\) ,也就是说:

\[ y = x \cdot (\frac {2}{\pi }) \]

即:

\[ k = \left \lfloor y \right \rfloor \]

那么所求浮点数的尾数部分:

\[ f = y − k \]

最终可得到归约之后的结果 \(\large {r}\)

\[ r = f \cdot (\frac {\pi }{2}) \]

回到我们的目标,我们需要知道 \(\large {k}\) 的值 和 \(\large {r}\) 的值!

那我们能直接用上述公式计算吗?

我们知道 \(\large {\pi}\) 是超越数,是无法用二进制表示的,在计算机里只能去近似。我们最终要求得的三角函数的误差取决于下面几个方面:

  1. 使用多少位数的 \(\large {\pi}\) 近似值;
  2. 参数归约时产生的误差;
  3. 计算参数归约之后的三角函数时的误差。

对于输入参数 \(\large {x}\) 不是很大的情况,误差主要由参数归约时产生的误差决定,而当输入参数 \(\large {x}\) 很大的情况,参数归约产生的误差就不再是主要因素了!

计算 \(\large {k}\)

由之前的推导,我们知道:

\[ k = \left \lfloor y \right \rfloor = x \cdot (\frac {2}{\pi }) \]

但是由于浮点数的精度限制,我们知道对于 \(\large {x}\) 很大情况,我们不能直接去计算!

由三角函数关系可知,我们实际上只需要计算 \(\large {k \% 4}\) 的值即可,也就是说只需要知道 \(\large {k}\) 的最后 \(\large {2}\) 个 二进制位值即可,这样就可以节省大量运算了!

让我们回到 浮点数标准 11 ,以 \(\large {32}\) 位单精度浮点数为例,其值可以表示为:

\[ x = (-1)^{b_{31}} \times 2^{(b_{30}b_{29}\dots b_{23})_{2} - 127} \times (1.b_{22}b_{21}\dots b_{0})_{2} \]

即为:

\[ \text {value} = (-1)^{\text {sign}} \times 2^{(E - 127)} \times \left (1 + \sum _{i=1}^{23}b_{23-i} 2^{-i} \right) \]

(原始论文和数值分析具体实现代码太难看懂了,这篇文章写了快 1 个月了!:-( )

小结

这是目前我对参数归约(Argument Reduction) 算法的理解,后续有新的发现、感悟都会更新此文章。

参考文献


  1. CORDIC算法:一种高效计算三角函数值的方法↩︎

  2. 泰勒展开式(Taylor series)↩︎

  3. Exponentiation by squaring↩︎

  4. Binary number↩︎

  5. Arithmetic logic unit↩︎

  6. 计算机算术↩︎

  7. Numerical Analysis↩︎

  8. 泰勒展开式(Taylor series)↩︎

  9. W. Cody and W. Waite, Software Manual for the Elementary Functions, Prentice-Hall, Englewood Cliffs, N.J., 1980.↩︎

  10. M. Payne and R. Hanek, “Radian Reduction for Trigonometric Functions”, Signum, p19-24, Jan 1983.↩︎

  11. IEEE 754↩︎