不断重复这一过程,可知当 f 的 n+1 阶导数连续时,有:
其中积分余项 为:
1 | /** |
1 | /** |
1 | /* @(#)k_sin.c 1.3 95/01/18 */ |
假如你知道浮点数的话,你就知道为什么了!
按照 IEEE 754 浮点数标准 制定的 浮点数运算法则, float 类型的单精度浮点数 的尾数部分有 位二进制数,如下图所示:
在十进制下,大致相当于 ,有效数字大约有 位。
所以当 时,我们应该使用 double 类型的双精度浮点数 [^12] ,这样才能保证结果有足够的精度!
双精度浮点数的尾数部分有 位,如下图所示:
在十进制中大致相当于 ,也就是说当 有效数字是 时,我们应该使用 double 类型的双精度浮点数可以保证精度!
但这仍然有个问题,那就是 有效数字 超过 位,应该怎么办?
(挖坑!)
A系列纸张尺寸的长短比(宽高比)都是 ,然后舍去到最接近的毫米值。A0
之前写过一篇介绍 CORDIC 算法 [1] 的文章,里面提到 CORDIC 算法的 不足 之处,CORDIC 算法的输入角度范围需要在 ,那么我们不禁要问,如果输入角度 很大的话,怎么处理呢?
这个问题同样存在于 泰勒展开式(Taylor series) [2] 中,比如 和 的泰勒展开式:
虽然在整个实数集 都成立,但是在实际应用中因为展开项数限制和浮点数的精度限制, 的范围只有在接近 的时候才有比较高的精度。
但是实际应用中,如果输入 很大的话,比如 情况下怎么得到足够精确的值呢?
中学里我们知道三角函数是周期函数,对于比较大的值,我们可以使用下面的公式将值归约到一个比较小的范围内。
这就是我们今天要讲的 参数归约(Argument Reduction) 算法。
参数归约 听起来就很唬人,什么是参数啊,什么归约啊,都是些高大上的名词,听起来云里雾里的!
为了不让大家产生厌倦和畏难心理,我们先从一道小学数学计算题开始:
不借助计算器,计算 的值!
对于这道题,大家可能会列出下列算术:
但其实呢,我们也可以使用下面的方法:
如果我说上面这 种方法都用到了参数归约的思想,你可能会感到震惊,什么?这种小学计算题也用到了参数归约算法吗?
上一章计算 时,我们将 化简为 ,再在结果后面直接加上 个 ,那么你有没有想过这背后隐含了什么数学思想吗?
下面我们正式进入今天的课题:参数归约(Argument Reduction) 。
为了提高数学函数的计算效率,将初始问题转变或者说缩小到函数更容易计算的域内,这就是参数归约。
已知函数 ,求 的值,可以通过以下 个步骤进行计算:
现在回到上一节的小学数学计算题,我们实际上用到了 种参数归约:
实际上为了让幂级数更快地收敛,通常我们取 以获得双倍公式,比如 ,比如 快速幂算法(Exponentiation by squaring) [3] , 其具体实现可参考这篇文章: Fast Power Algorithm: Binary Exponentiation 。
而计算器中也常用到三倍角公式 去计算三角函数值,具体可参考这个视频: 计算器是如何计算出三角函数和对数的? 。
可能有同学会问,那二倍角公式 就不用了吗?这个谜底待后续章节介绍。
这一章我们来讲如何进行参数归约,通常我们区分 种参数归约:
这种归约可以应用在 是周期函数的情况,比如三角函数,此时 ;也可以应用于其他函数,比如小学数学我们知道计算 就是看有多少个 相加小于等于 ,具体可参考这篇文章:29. Divide Two Integers 。
应用于计算指数函数 时,其中 。
值得注意的是,对于给定的函数,两种参数归约方式都可能使用。例如,对于 ,我们既可以使用三倍角公式 化简,也可以使用加法归约 。
通过上面的分析,现在让我们去计算任意输入 的 、 的值,可以分为下面 种情况:
听起来似乎很简单,但事实上远远没有这么容易!
我们的电脑是基于 二进制(Binary) [4] 的,本质只是高电平和低电平在电路上切换运行而已。因为 CPU 种的 逻辑运算单元(Arithmetic logic unit) [5] 只能做加法和移位操作,因此而诞生了 计算机算术(Computer Arithmetic) [6] 这门学科!
数学中有一门学科 数值分析(Numerical Analysis) [7] 就是专门研究各种计算的!
虽然三角函数的周棋是 ,但实际上我们只用归约到 即可,这里大家可以想想为什么?
之前我以为数值运算对于 的参数,会使用 CORDIC 算法,但实际上我看了一些数值计算库,发现对于 还是使用泰勒(Taylor Series) [2:1] 逼近,当然里面用了很多技巧,大家可以看看库函数的具体实现即可解惑(这里先挖个坑,等我彻底看懂了再来这里填坑!)。
那对于 ,如何计算呢?
我们可以使用下列公式将 归约到 :
我们可以很容易按照上述思想写出对应的代码,这就是 Cody & Waite 提出的 Cody-Waite 归约算法[8] 。
但是如果你认为这样就高枕无忧了的话,就太早了!
假如输入 的话,上面的方法就会失效!想一想为什么?
上一章提出了一个问题,有效数字 超过 位的超大数字该如何计算呢?针对这个问题, Payne 与 Hanek [9] 提出把浮点运算转换为大整数运算,来解决超大数字的浮点数归约问题。
要弄懂 Payne-Hanek 归约算法,需要对数学有比较深的理解,下面一步一步来分析!
对于输入 :
两边同乘 ,可得:
因为 , ,也就是说:
即:
那么所求浮点数的尾数部分:
最终可得到归约之后的结果 :
回到我们的目标,我们需要知道 的值 和 的值!
那我们能直接用上述公式计算吗?
我们知道 是超越数,是无法用二进制表示的,在计算机里只能去近似。我们最终要求得的三角函数的误差取决于下面几个方面:
对于输入参数 不是很大的情况,误差主要由参数归约时产生的误差决定,而当输入参数 很大的情况,参数归约产生的误差就不再是主要因素了!
由之前的推导,我们知道:
但是由于浮点数的精度限制,我们知道对于 很大情况,我们不能直接去计算!
由三角函数关系可知,我们实际上只需要计算 的值即可,也就是说只需要知道 的最后 个 二进制位值即可,这样就可以节省大量运算了!
让我们回到 浮点数标准 [10] ,以 位单精度浮点数为例,其值可以表示为:
即为:
(原始论文和数值分析具体实现代码太难看懂了,这篇文章写了快 1 个月了!😦 )
这是目前我对参数归约(Argument Reduction) 算法的理解,后续有新的发现、感悟都会更新此文章。
W. Cody and W. Waite, Software Manual for the Elementary Functions, Prentice-Hall, Englewood Cliffs, N.J., 1980. ↩︎
M. Payne and R. Hanek, “Radian Reduction for Trigonometric Functions”, Signum, p19-24, Jan 1983. ↩︎
引言:
在机器学习和优化算法的领域中,梯度下降法被广泛应用于解决各种问题,从训练神经网络到参数优化。这种强大的优化算法通过不断迭代更新参数,以最小化目标函数或最大化收益函数。本文将介绍梯度下降法的数学原理,探讨其在实际应用中的广泛应用以及其优点和不足之处。
梯度下降法的核心思想是通过计算目标函数的梯度,并沿着梯度的反方向迭代地更新参数,以逐步逼近最优解。具体而言,梯度下降法包括以下步骤:
随机初始化参数向量。
计算目标函数在当前参数向量处的梯度。
沿着梯度的反方向更新参数向量。
重复以上步骤,直到满足停止条件(如达到最大迭代次数或参数变化不显著)。
梯度下降法在各个领域都有广泛的应用,以下是一些常见的应用实例:
梯度下降法具有以下优点:
然而,梯度下降法也存在一些不足之处:
梯度下降法是优化算法中的魔法箭头,它通过迭代更新参数的方式,在机器学习和优化领域取得了巨大成功。虽然存在一些挑战和局限性,但梯度下降法仍然是解决各种实际问题的重要工具,为我们优化世界的探索提供了强大的支持。
无限猴子定理(英语:Infinite monkey theorem)
让一只猴子在打字机上随机地按键,当按键时间达到无穷时,几乎必然能够打出任何给定的文字,比如莎士比亚的全套著作。
在这里,几乎必然是一个有特定含义的数学术语,“猴子”也不是一只真正意义上的猴子,它被用来比喻成一个可以产生无限随机字母序列的抽象设备。这个理论说明把一个很大但有限的数看成无限的推论是错误的。猴子精确地通过键盘敲打出一部完整的作品比如说莎士比亚的哈姆雷特,在宇宙的生命周期中发生的概率也是极其低的,但并不是零。
遗传算法(Genetic Algorithm) [1] 是一种元启发式搜索和优化技术,借鉴了生物进化中的自然选择和遗传机制。它已经在各个领域展示出了强大的应用潜力。本文将介绍遗传算法的发展历史、原理、示例,以及其广泛应用和不足之处。
遗传算法的发展可以追溯到上世纪60年代的约翰·荷兰德(John Holland)和他的同事们的工作。他们首先提出了基因型与表现型之间的映射关系,并通过模拟生物进化过程来解决复杂的优化问题。
遗传算法的核心原理是模拟自然进化的过程。它通过定义适应度函数来评估候选解的质量,并利用遗传操作(选择、交叉和变异)对候选解进行迭代改进。具体而言,算法从一个初始种群开始,通过选择适应度较高的个体作为父代,进行交叉和变异操作产生新的后代个体,然后通过评估适应度来选择出下一代个体。这个过程不断迭代,直到找到满足特定条件的优秀解。
It’s never too late
目前参考网络资源写了一个简单的Demo,地址:http://longluo.me/projects/genetic
这个例子还有待完善!
遗传算法在各个领域都有广泛的应用。它被用于优化问题、机器学习、数据挖掘、调度问题等。例如,在工程设计中,可以使用遗传算法来优化设计参数,以获得更好的性能。在人工智能领域,遗传算法被用于训练神经网络的参数。
遗传算法在应用中存在一些缺点,但可以采取一些方法来规避这些问题。以下是一些常见的遗传算法缺点及其应对措施的例子:
可能陷入局部最优解:由于遗传算法的随机性质,可能导致算法收敛到局部最优解而无法达到全局最优解。为了规避这个问题,可以采用以下方法:
多次运行算法:运行遗传算法的多个独立实例,以增加搜索空间的探索性。
引入多样性维护机制:通过保留一部分较差个体、增加变异操作的概率或引入新的个体,以增加种群的多样性,避免过早陷入局部最优解。
计算复杂度高:遗传算法需要进行大量的计算和评估操作,导致计算复杂度较高。为了降低计算成本,可以尝试以下方法:
优化评估函数:通过对评估函数进行优化,减少计算成本。
使用近似算法:在某些情况下,可以使用近似算法或启发式方法来加速计算过程,以减少计算复杂度。
参数选择困难:遗传算法中的参数选择对算法的性能和效果至关重要。不正确的参数选择可能导致算法效果不佳。为了解决这个问题,可以采取以下措施:
参数调优:通过实验和测试,调整算法的参数,以找到最佳的参数组合。
自适应参数调整:引入自适应机制,根据问题的特性和算法的表现,动态调整参数值。
举例来说,假设有一个优化问题,需要找到一个函数的最小值。遗传算法被用来解决这个问题,但在初步运行中发现算法很容易陷入局部最优解。为了规避这个问题,可以采取多次运行算法的方法,每次使用不同的初始种群和随机数种子,以增加搜索空间的覆盖度,从而更有可能找到全局最优解。
另外,遗传算法在每一代选择操作时都会考虑适应度函数的评估,这可能导致计算复杂度的增加。为了降低计算成本,可以对适应度函数进行优化,使用更高效的算法或数据结构进行计算。同时,可以利用并行计算或分布式计算的方法,提高算法的计算效率。
总之,通过合理的方法和技巧,可以规避遗传算法的一些缺点,并提高算法的效率和性能。根据具体的问题和应用场景,选择适当的策略和方法来克服遗传算法的局限性。
综上所述,遗传算法作为一种强大的优化技术,在解决复杂问题和优化搜索中具有广泛的应用。通过模拟生物进化的机制,它能够发现优秀的解决方案。然而,它也面临局部最优解和计算复杂度高的挑战。随着计算能力和算法改进的不断提升,遗传算法在未来将继续发挥重要作用,并为各个领域的问题提供创新解决方案。
任何一款科学计算器上都可以计算三角函数,三角函数应用于生活工作中的方方面面,但计算机是如何计算三角函数值的呢?
三角函数本质上是直角三角形的3条边的比例关系,计算并没有很直观,那么计算机是如何计算三角函数值的呢?
在微积分中我们学习过 泰勒公式 ,我们知道可以使用泰勒展开式来对某个值求得其近似值,例如:
利用泰勒公式,取前 项:
代入可得:
可见取前 项时精度已经在 之下,对于很多场合精度已经足够高了。
在没有了解 CORDIC(Coordinate Rotation Digital Computer) Algorithm [1] 算法之前,我一直以为计算器是利用泰勒公式去求解,最近才了解到原来在计算机中,CORDIC 算法远比泰勒公式高效。
下面我们就来了解下泰勒公式的不足之处和 CORDIC 算法是怎么做的。
上一节我们使用泰勒公式去计算三角函数值时,需要做很多次乘法运算,而计算器中乘法运算是很昂贵的,其缺点如下所示:
在之前的文章 矩阵乘法的 Strassen 算法 ,就是通过减少乘法,多做加法,从而大大降低了运算量,那么我们可以用相同的思想来优化运算吗?
当然可以,让我们请出今天的主角:CORDIC 算法。
我们知道单位圆上一点 ,其坐标为: ,如下图所示:
如果我们接收一个旋转向量 逆时针旋转 ,将点 旋转到 , 如下图所示:
很容易得到如下公式:
实际上由 复数运算 ,我们知道复数乘法就是幅角相加,模长相乘。我们可以将上式写成下列矩阵运算形式:
但上式运算时,只是对向量 进行了线性变换,乘以一个旋转向量 ,得到了旋转后的结果:向量 。
但是上式仍然需要 次乘法和 次加减法操作, 复杂度没有任何降低,那怎么办呢?
当当…当!
通过上述分析,我们已经知道可以使用有限次旋转操作来避免复杂的乘法操作,我们修改矩阵运算公式,提取 ,则公式可以修改为:
如果我们选择合适的角度值 ,使得
这样和 乘法操作就变成了移位操作,我们知道计算机中移位操作是非常快的,就可以大大加快计算速度了。
但这里仍然有 个问题需要解决:
对于第一个问题,答案是否定的。可以从数学上证明只有 的倍数角才可以得到完全一致的结果。但是在工程应用中,我们只需要满足一定精度即可,可以增加迭代次数无限逼近原始角度,如下所示提高 值以无限逼近原始角度。
对于第二个问题,我们先来个例子,以 为例来看看求解过程:
那么第一次旋转:
第二次旋转:
第三次旋转:
综合可得:
因为 ,由三角公式可以计算出:
令 ,则当进行 次迭代之后:
当 越来越小时, 也越来越逼近 ,当迭代次数 , 极限存在,求解可得:
由 我们实际上可以得到最终的向量 的模长极限为:
实际上当迭代次数为 时,可以计算出缩放比例 ,就已经精确到 了,如下所示:
实际上,任意角度只要迭代次数超过 ,我们可以直接使用 这个值。
对于第三个问题,稍微有点复杂,我们在下一节继续讲解!
上一节遗留的问题是迭代旋转角度时,旋转角度不一定会落在目标角度内,我们需要引入一个角度误差,用来衡量旋转角度和目标角之间距离,如下所示:
当 时,我们应该逆时针旋转,而 ,则顺时针旋转。根据精度需要,当 即可退出迭代。
同时我们修改之前的公式,引入 ,于是可以得到最终公式:
上面讲了这么多,来个实例吧,练习巩固下知识,看看自己是否真的懂了?
计算 和 的值。
从 开始,迭代 次结果如下:
第 次迭代 | ||||
---|---|---|---|---|
- | - | |||
迭代到第 次时,角度误差已经小于 了, 通过表格可知:
通过计算器可知, , ,误差已经在 之下了,实际应用中我们会迭代 次,误差会非常小。
通过上面分析,我们已经知道了 CORDIC 算法的原理,下面就开始编程吧!
用 JavaScript 写了一个在线互动版本,传送门 → :
CORDIC 算法相比其他算法的优点,体现在以下几个方面:
几个月这篇文章发布之后,陆陆续续得到了不少读者的宝贵意见,有读者反馈说没有写 CORDIC 算法的缺点,也有读者反馈在他在嵌入式处理器STM32上还不如 泰勒展开式(Taylor Series) 快。
对于这 个问题,我又查阅了一些 CORDIC 算法[2] 的资料,可以回答下这 个问题。
CORDIC 算法的不足之处在于下面几点:
CORDIC 算法的收敛速度较慢,比如当输入值 很接近 或 时,这个时候就需要进行多轮迭代才能达到足够的精度。
当 时,实际上只需要取前 项就有足够精度了,而且收敛速度快多了!
如上一小节,当输入值 是小角度或者大角度的情况时,CORDIC 算法的固定迭代次数可能也无法提供足够的精度。
CORDIC 算法在实际应用中需要考虑输入参数的限制 ,特别是幅值和角度范围,这是因为 CORDIC 算法是一种迭代算法,对于较大的角度,需要更多的迭代次数才能达到所需的精度,从而导致计算效率的下降。
CORDIC 算法适用于处理相对较小范围的角度,确切来说是在 的范围内表现最佳,下面我们来证明下:
由 CORDIC 算法计算过程可知:
那么可得:
假设 , 则有:
因此
所以 CORDIC算法的输入范围是: 。
对于第 个问题,我不太清楚那位读者是如何测试的!如之前所述,在某些情况下,泰勒展开式是优于 CORDIC 算法的!
CORDIC 算法是在硬件中实现的,是 FPGA 设计[3] 中的经典例题。
CORDIC 算法是一种高效计算三角函数值的方法。相比传统的泰勒展开式,它具有简单高效、低存储需求和迭代控制等优势。在需要计算三角函数值的应用中,CORDIC 算法更快速、更节省资源。
最近在书店里看到一本 《数学的奥秘》 ,原著是 《The Secret Life of Equations: The 50 Greatest Equations and How They Work》 ,讲的是最伟大的数学方程式起源、构成、含义和应用。书的内容并不多,我看了其中的一部分,里面有讲 墨卡托投影(Mercator projection)[1] 。
我对地理和地图一直很感兴趣,但之前对原理一直一知半解的,只知道我们常见的地图都是墨卡托投影,由墨卡托[2] 在 1569年创立。但墨卡托投影的原理是什么却并不清楚。书里面只有几页,但具体公式缺乏说明及推导过程,所以这几天通过查找资料掌握了墨卡托投影的原理。
在大航海时代,航海家可以通过六分仪和观察日月星辰获取到经纬度,但在茫茫大海中迷失方向是很可怕的事情,如何才能正确的航行到目的地呢?
地球由于自转是一个两极比赤道略短的扁球体,但扁率约为 ,非常之低,因为可以视为球体。因为球面不是可展曲面,也就是如果展成平面的话,长度会发生形变,所以也会形变。因为根据高斯绝妙定理(Gauss theorem egregium)[3] ,平面的高斯曲率为 ,而球面的高斯曲率为 ,其中 为球面半径,所以在保持图形完整性前提下,将球面转化为平面,投影后得到的经纬线网形状必然会产生变形,也就是说,在投影的过程中,变形是必然存在的。
在这种情况下,墨卡托运用等角圆柱投影法绘制了航海图,极大地方便了航海家。有了墨卡托海图,航海家想要到达某个目的地,只需要在出发点和目的地之间连一条直线,量出这条航线和经线的夹角,按照航行路线就能到达目的地。
设想将地球置于在一中空的圆柱里,如下图所示,其基准纬线(赤道)与圆柱相切。再设想地球中心处放置一灯泡,那么从球心处发射的光线会把球面上的图形投影到圆柱体上,之后再将把圆柱体展开,那么就可以得到一张墨卡托投影绘制出的地图。
设 表示投影前的球面上的点,在数学上我们常使用弧度制,则表示为 ,其中 , ,所以墨卡托投影就是将球面上的 转换为平面直角坐标系 ,即定义为下面的一个映射:
那么则有函数 和 :
如下图,图中由经纬线分割出的方块区域 映射为 ,如果 和 点足够近的话,那么 可以近似为矩形。
的经纬度为 ,相邻点 的经纬度为 ,那么平行赤道的纬线所在圆的半径 ,则有圆弧:
,圆弧 。投影变换后平面上的矩形长宽分别为 和 。
在变换后,在微观局部应该保持长宽比例不变,那么:
另外也可以根据墨卡托投影是等角投影[4], ,则有:
那么:
显然同一条经线上的点,变换到墨卡托之后横坐标相同,而横坐标的值就是赤道弧线划过的长度,所以 ,那么 。
消掉 和 ,可得:
对上式积分可得:
可得:
根据初始条件可知 ,所以等角投影的公式:
之前讲过墨卡托投影的地图上,经线投影成一组平行线,两地之间的等方位角曲线(Rhumb line),在地图上是一条直线。航海家只需要保持方位角不变,不改变航线即可达到终点,所以在航海中得到广泛应用。但这条航线并不是两地之间的最短航线,我们知道球面上两点间最短距离是通过两点间大圆的劣弧,如下图所示。在航海或航空中,运用此特性而走最短距离的航线叫做大圆航线(Great Circle Route)。