数学函数
从这一章开始,我们将开始探索使用 Owl 进行数字计算的世界。但让我们不要过于仓促。在进入诸如算法微分、优化、计算图等主要内容之前,让我们先尝一些开胃菜。在本章中,我们将介绍 Owl 中如何支持熟悉的数学运算。本章根据不同类型的函数进行组织,您可以随意浏览其中任何部分。请注意,本章中的函数适用于标量值。在后续章节中介绍的 N 维数组模块包含这些在 n 维数组上工作的基本函数,包括向量和矩阵。
基本函数
基本一元数学函数
许多基本数学函数将一个浮点数作为输入并返回一个浮点数。我们称它们为一元函数。您可以轻松使用这些一元函数从Maths
模块中调用。例如:
Maths.sqrt 2.
>- : float = 1.41421356237309515
[@tbl:maths:basic_unary] 列出了在该模块中支持的这些一元函数。
函数 | 解释 |
---|---|
abs |
|x| |
neg |
-x |
reci |
1/x |
floor |
the largest integer that is smaller than x |
ceil |
the smallest integer that is larger than x |
round |
rounds x towards the bigger integer when on the fence |
trunc |
integer part of x |
sqr |
\(x^2\) |
sqrt |
\(\sqrt{x}\) |
基本二元数学函数
与一元函数不同,二元函数接受两个浮点数作为输入,并返回一个浮点数作为输出。大多数常见的算术函数属于这个类别,如[@tbl:maths:binary]所示。
函数 | 解释 |
---|---|
add |
x + y |
sub |
x - y |
mul |
x * y |
div |
x / y |
fmod |
x % y |
pow |
\(x^y\) |
hypot |
\(\sqrt{x^2 + y^2}\) |
atan2 |
返回\(\arctan(y/x)\),考虑参数的符号;这是向量\((x, y)\)相对于 x 轴的角度。 |
指数和对数函数
常数\(e = \sum_{n=0}^{\infty}\frac{1}{n!}\)是我们称之为自然常数的东西。之所以这样称呼它,是因为指数函数及其逆函数对数在自然界和我们的日常生活中被频繁使用:对数螺线、人口增长、碳测古物、计算银行投资等等。举个例子,在关于细菌的科学实验中,我们可以假设时间\(t\)时的细菌数量遵循指数函数\(n(t) = Ce^rt\),其中\(C\)是初始人口,\(r\)是每日增长率。通过这个模型,我们可以预测在一定时间内细菌的增长情况。
我们还有这个美妙的欧拉公式,它连接了两个最常用的常数和复数和自然数的底:
\[e^{i\pi}+ 1=0.\]
指数和对数函数的完整列表,以及一些方便的变体,都在[@tbl:maths:explog]中呈现。
函数 | 解释 |
---|---|
exp |
指数函数\(e^x\) |
exp2 |
\(2^x\) |
exp10 |
\(10^x\) |
expm1 |
返回\(\exp(x) - 1\),但对于\(x \sim 0\)更精确 |
log |
\(\log_e~x\) |
log2 |
\(\log_2~x\) |
log10 |
\(\log_{10}~x\) |
logn |
\(\log_n~x\) |
log1p |
逆函数expm1 |
logabs |
\(\log(|x|)\) |
xlogy |
\(x \log(y)\) |
xlog1py |
\(x \log(y+1)\) |
logit |
\(\log(p/(1-p))\) |
expit |
\(1/(1+\exp(-x))\) |
log1mexp |
\(\log(1-\exp(x))\) |
log1pexp |
\(\log(1+\exp(x))\) |
三角函数
正弦、余弦和正切函数属于三角函数,它们将两条边的比率与直角三角形的角度联系起来。它们包括常用的sin
和cos
等,以及它们的倒数,如余割、正割等。这些函数在不同领域的数值计算应用中广泛使用,如力学和几何学。这些三角函数都是一元函数,例如:
Maths.sin (Owl_const.pi /. 2.)
>- : float = 1.
它们都包含在 Owl 的数学模块中,如[@tbl:maths:triangular]所示。
函数 | 解释 | 导数 | 泰勒展开 |
---|---|---|---|
sin |
\(\sin(x)\) | \(\cos(x)\) | \(\sum_{n=1}(-1)^{n+1}\frac{x^{2n+1}}{(2n+1)!}\) |
cos |
\(\cos(x)\) | \(-\sin(x)\) | \(\sum_{n=1}(-1)^n\frac{x^{2n}}{(2n)!}\) |
tan |
\(\tan(x)\) | \(1 + \tan^2(x)\) | \(\sum_{n=1}\frac{4^n(4^n-1)B_n~x^{2n-1}}{(2n)!}\) |
cot |
\(1/\tan(x)\) | \(-(1 + \textrm{cot}^2(x))\) | \(\sum_{n=0}\frac{E_n~x^{2n}}{(2n)!}\) |
sec |
\(1/\cos(x)\) | \(\textrm{sec}(x)\tan(x)\) | \(\sum_{n=0}\frac{2(2^{2n-1})B_n~x^{2n-1}}{(2n)!}\) |
csc |
\(1/\sin(x)\) | \(-\textrm{csc}(x)\textrm{cot}(x)\) | \(\frac{1}{x}-\sum_{n=1}\frac{4^n~B_n~x^{2n-1}}{(2n)!}\) |
这里的\(B_n\)是第\(n\)个贝努利数,\(E_n\)是第\(n\)个欧拉数。图[@fig:algodiff:trio]显示了这些三角函数之间的关系。这个图表受到了维基百科的一篇文章的启发。这些函数还有相应的反函数:asin
、acos
、atan
、acot
、asec
、acsc
。例如,如果\(\sin(a) = b\),那么\(\textrm{asin}(b) = a\)。

另一个相关的概念是双曲函数,如sinh
和cosh
。这些函数是使用指数函数定义的。正如我们在[@#fig:algodiff:trio]中看到的,三角函数与圆相关。类似地,双曲函数与双曲线相关。例如,点(cosh(x), sinh(x))
形成等边双曲线的右半部分,就像圆上的(cos(x), sin(x))
一样。双曲函数广泛应用于数值计算中,如微分方程解、双曲几何等。
sinh
: \(\frac{e^x - e^{-x}}{2}\), derivative is \(\cosh(x)\), and taylor expansion is \(\sum_{n=0}\frac{x^{2n+1}}{(2n+1)!}\).cosh
: \(\frac{e^x + e^{-x}}{2}\), derivative is \(\sinh(x)\), and taylor expansion is \(\sum_{n=0}\frac{x^{2n+1}}{(2n+1)!}\).tanh
: \(\frac{\sinh{x}}{\cosh{x}}\), derivative is \(1-\tanh^2(x)\), and taylor expansion is \(\sum_{n=1}\frac{4^n(4^n-1)B_{2n}~x^{2n-1}}{(2n)!}\).coth
: \(\frac{\cosh{x}}{\sinh{x}}\), derivative is \(1-\coth^2(x)\), and taylor expansion is \(\frac{1}{x}-\sum_{n=1}\frac{4^n~B_{2n}~x^{2n-1}}{(2n)!}\).sech
: \(1/\cosh(x)\), derivative is \(-\tanh(x)/\cosh(x)\), and taylor expansion is \(\sum_{n=0}\frac{E_{2n}~x^{2n}}{(2n)!}\).csch
:\(1/\sinh(x)\), derivative is \(-\coth(x)/\sinh(x)\), and taylor expansion is \(\frac{1}{x}+\sum_{n=1}\frac{2(1-2^{2n-1})B_{2n}~x^{2n-1}}{(2n)!}\).
同样地,每个这些函数都有对应的逆函数:asinh
、acosh
、atanh
、acoth
、asech
、acsch
。这些双曲三角函数之间的关系在[@fig:algodiff:hyper_trio]中清晰地描绘出来。

除了这些函数外,还有一些相关的函数。 sinc
返回 \(\sin(x)/x\) 并且在 \(x=0\) 时为 1。 logsinh
返回 \(\log(\sinh(x))\) 但处理大的 \(|x|\)。 logcosh
返回 \(\log(\cosh(x))\) 但处理大的 \(|x|\)。 sindg
/cosdg
/tandg
/cotdg
是以度为单位给定的角度的正弦/余弦/正切/余切。
其他数学函数
还有一些在传统数学中可能不太常用的其他函数。像 sigmoid
和 relu
这样的函数在深度学习中经常用作神经网络中的激活函数。激活函数对于神经网络在输出结果、准确性、收敛速度等方面都至关重要。我们将在本书后面的神经网络章节中详细讨论它们。
sigmoid x
: \(1 / (1 + \exp(-x))\)signum x
: 返回x
的符号:-1、0 或 1softsign x
: 平滑sign
函数relu x
: \(\max(0, x)\)
特殊函数
除了我们刚刚列出的,还有很多特殊函数。你可能以前没听说过它们,但它们在数学分析、物理学等不同领域都有确立的名称,对科学家和工程师很重要。在 Owl 中,这些函数的实现依赖于Cephes 数学函数库,这是一个包含对科学家和工程师感兴趣的特殊函数的 C 语言库。它们在本节的其余部分中列出。也许我们不能深入挖掘所有这些函数的数学或物理含义,但在需要时您可能会发现它们非常方便。
艾里函数
艾里函数 Ai(x)
以英国天文学家乔治·B·艾里的名字命名。它是二阶线性微分方程的解:
\[y''(x) = xy(x).\]
这个微分方程有两个线性无关的解 Ai
和 Bi
。Owl 提供 airy
函数来执行此操作:
val airy : float -> float * float * float * float
返回的四个数字分别是 Ai
、它的导数 Ai'
、Bi
和它的导数 Bi'
。让我们看一个例子。它绘制了两个解 Ai
和 Bi
在[@fig:algodiff:airy]中。
let x = Mat.linspace (-15.) 5. 200
let y0 = Mat.map (fun x ->
let ai, _, _, _ = Maths.airy x in ai
) x
let y1 = Mat.map (fun x ->
let _, _, bi, _ = Maths.airy x in bi
) x
let _ =
let h = Plot.create "special_airy.png" in
Plot.(plot ~h ~spec:[ RGB (66, 133, 244); LineStyle 1; LineWidth 2. ] x y0);
Plot.(plot ~h ~spec:[ RGB (219, 68, 55); LineStyle 2; LineWidth 2. ] x y1);
Plot.(set_yrange h (-0.5) 1.);
Plot.(legend_on h ~position:SouthEast [|"Ai"; "Bi"|]);
Plot.output h

贝塞尔函数
贝塞尔函数由数学家丹尼尔·贝努利首次定义,然后由弗里德里希·贝塞尔进行了泛化,它们是贝塞尔微分方程的规范解:
\[x^2y''+xy'+(x^2 - \alpha^2)y = 0.\]
复数\(\alpha\)被称为贝塞尔函数的“阶”。贝塞尔函数在研究波传播和静电势等许多问题中非常重要,例如在圆柱波导中的电磁波。在解决柱坐标系时,通常使用整数阶或半整数阶的贝塞尔函数。
贝塞尔函数可以分为两种。两种都是贝塞尔微分方程的解,但第一种在原点处是非奇异的,而第二种在原点处是奇异的(\(x=0\))。特殊情况是当\(x\)是纯虚数时。在这种情况下,解被称为修改的贝塞尔函数,它也可以被归类为第一种和第二种。基于这些类别,Owl提供了这些函数。
函数 | 解释 |
---|---|
j0 x |
零阶第一类贝塞尔函数 |
j1 x |
一阶第一类贝塞尔函数 |
jv x y |
实阶第一类贝塞尔函数 |
y0 x |
零阶第二类贝塞尔函数 |
y1 x |
一阶第二类贝塞尔函数 |
yv x y |
实阶第二类贝塞尔函数 |
yn a x |
整数阶第二类贝塞尔函数 |
i0 x |
零阶修改贝塞尔函数 |
i1 x |
一阶修改贝塞尔函数 |
iv x y |
实阶修改贝塞尔函数 |
i0e x |
零阶指数缩放修改贝塞尔函数 |
i1e x |
一阶指数缩放修改贝塞尔函数 |
k0 x |
零阶第二类修改贝塞尔函数 |
k1 |
一阶第二类修改贝塞尔函数 |
k0e |
零阶指数缩放第二类修改贝塞尔函数 |
k1e |
一阶指数缩放第二类修改贝塞尔函数 |
椭圆函数
Jacobi椭圆函数用于研究摆动运动。共有十二个Jacobi椭圆函数,而我们在这里包括的ellipj
返回其中三个:sn
、cn
和dn
。该函数的第四个输出phi
被称为输入u
的振幅。
另一方面,椭圆积分最初用于找到椭圆的周长。椭圆积分函数可以表示为:\[f(x)=\int_c^xR(t, \sqrt{P(t)})dt,\]其中\(R\)是其两个参数的有理函数,\(P\)是次数为3或4的多项式,没有重复的根,\(c\)是一个常数。椭圆积分可以分为“完全”或“不完全”。前者是一个单一参数的函数,而后者包含两个参数。每个椭圆积分都可以变换,使其包含有理函数和三个Legendre标准形式的积分,根据这些形式,椭圆可以分为第一、第二和第三类。Owl中的椭圆函数列在[@tbl:maths:elliptic]中。
函数 | 解释 |
---|---|
ellipj u m |
参数m 在0和1之间,实参数u 的Jacobi椭圆函数 |
ellipk m |
完全椭圆积分的第一类 |
ellipkm1 p |
围绕m = 1的完全椭圆积分的第一类 |
ellipkinc phi m |
第一类不完全椭圆积分 |
ellipe m |
完全椭圆积分的第二类 |
ellipeinc phi m |
第二类不完全椭圆积分 |
我们可以使用ellipe
计算椭圆的周长。通常,要计算这个需要微积分,但椭圆函数提供了一个简单的解决方案。假设一个椭圆的半长轴\(a=4\),半短轴\(b=3\)。我们可以简单地使用\(4a\textrm{ellipe}(1 - \frac{b^2}{a^2})\)来计算其周长。
let a = 4.
>val a : float = 4.
let b = 3.
>val b : float = 3.
let c = 4. *. a *. Maths.(ellipe (1. -. pow (b /. a) 2.))
>val c : float = 22.1034921607095072
伽玛函数
对于正整数n,伽玛函数是阶乘函数:
\[\Gamma(n) = (n-1)!\]
对于具有正实部的复数\(z\),伽玛函数定义为:
\[\Gamma(z) = \int_0^{\infty}x^{z-1}e^{-x}dx.\]
这里的伽玛函数是从零到无穷的积分。如果我们将其更改为从零到某个上限的积分,则称为“下不完全伽玛函数”。同样,如果是从某个下限到无穷的积分,则该积分称为“上不完全伽玛函数”。
伽玛函数在流体动力学、几何学、天体物理学等多个领域广泛应用。它特别适用于描述时间或空间中呈指数衰减的过程的普遍模式。Owl提供的伽玛函数及相关函数列在[@tbl:maths:gamma]中。
函数 | 解释 |
---|---|
gamma z |
返回 Gamma 函数的值 |
rgamma z |
Gamma 函数的倒数 |
loggamma z |
Gamma 函数对数的主支 |
gammainc a x |
正则化的下不完全 Gamma 函数 |
gammaincinv a y |
gammainc 的反函数 |
gammaincc a x |
补充不完全 Gamma 积分 |
gammainccinv a y |
gammaincc 的反函数 |
psi z |
digamma 函数 |
这里是使用 gamma
的一个例子。
let x = Mat.linspace (-3.5) 5. 2000
let y = Mat.map Maths.gamma x
let _ =
let h = Plot.create "example_gamma.png" in
Plot.(plot ~h ~spec:[ RGB (66, 133, 244); LineStyle 1; LineWidth 2. ] x y);
Plot.(set_yrange h (-10.) 20.);
Plot.output h

Beta 函数
Beta 函数定义为:
\[B(x,y) = \int_0^1t^{x-1}(1-t)^{y-1}dt = \frac{\Gamma(x)\Gamma(y)}{\Gamma(x+y)}\]
这里 \(\Gamma\) 是 Gamma 函数,类似于 Gamma 函数,Beta 函数也有其“不完全”版本。不完全 Beta 函数将此定义扩展为:
\[B(x, a, b) = \int_0^xt^{a-1}(1-t)^{b-1}dt.\]
在 Owl 中,可以使用 Maths
模块中的 beta x y
函数调用 Beta 函数,其不完全版本是 betainc a b x
。我们还提供了 betaincinv
函数,它是 betainc
的反函数。
Beta 函数有几个属性。例如,下面的代码显示了 beta 函数和 gamma 函数之间的关系。
let x = Maths.beta 3. 4.
>val x : float = 0.0166666666666666664
let y = Maths.((gamma 3.) *. (gamma 4.) /. (gamma (7.)))
>val y : float = 0.0166666666666666664
另一个属性是其对称性,即 \(B(x,y) = B(y, x)\)。
let x = Maths.beta 3. 4.
>val x : float = 0.0166666666666666664
let y = Maths.beta 4. 3.
>val y : float = 0.0166666666666666664
斯特鲁夫函数
斯特鲁夫函数定义如下:\[H_v(x) = (z/2)^{v + 1} \sum_{n=0}^\infty \frac{(-1)^n (z/2)^{2n}}{\Gamma(n + \frac{3}{2}) \Gamma(n + v + \frac{3}{2})},\]
其中\(\Gamma\)是伽玛函数。除非\(v\)是整数,否则\(x\)必须为正数。斯特鲁夫函数广泛应用于各种物理应用,如水波问题和非定常空气动力学计算等。
Owl函数struve v x
返回斯特鲁夫函数的值。参数\(v\)被称为此函数的阶。以下是显示阶从0到4的斯特鲁夫函数曲线的示例。
let _ =
let h = Plot.create "example_struve.png" in
Plot.(plot_fun ~h ~spec:[ RGB (66, 133, 244); LineStyle 1; LineWidth 2.] (Maths.struve 0.) (-12.) 12.);
Plot.(plot_fun ~h ~spec:[ RGB (219, 68, 55); LineStyle 2; LineWidth 2.] (Maths.struve 1.) (-12.) 12.);
Plot.(plot_fun ~h ~spec:[ RGB (244, 180, 0); LineStyle 3; LineWidth 2.] (Maths.struve 2.) (-12.) 12.);
Plot.(plot_fun ~h ~spec:[ RGB (77, 81, 57); LineStyle 1; LineWidth 2.] (Maths.struve 3.) (-12.) 12.);
Plot.(plot_fun ~h ~spec:[ RGB (111, 51, 129); LineStyle 2; LineWidth 2.] (Maths.struve 4.) (-12.) 12.);
Plot.(set_yrange h (-3.) 5.);
Plot.(legend_on h ~position:SouthEast [|"H0"; "H1"; "H2"; "H3"; "H4"|]);
Plot.output h

ζ函数
赫维茨 zeta 函数 zeta x q
定义如下:
\[\zeta(x, q) = \sum_{k=0}^{\infty}\frac{1}{(k+q)^x}.\]
当\(q\)设置为1时,该函数缩减为黎曼 zeta 函数。函数zetac x
返回黎曼 zeta 函数减1。ζ函数经常用于分析动态系统。此外,黎曼 zeta 函数在数论中发挥重要作用,并在量子物理、概率论和应用统计学等广泛应用。
我们可以在特定点评估ζ函数,例如:
Maths.zeta 4. 1.
>- : float = 1.08232323371113837
(Maths.pow Owl_const.pi 4.) /. 90.
>- : float = 1.08232323371113792
误差函数
这里的误差函数不是关于编程中的错误处理,而是另一类特殊函数。在数学中,误差函数定义为:\[\frac{2}{\sqrt{\pi}}\int_0^x e^{-t^2})dt.\]
误差函数经常出现在概率和统计学中。实际上,在统计学中,对于非负值x
,erf x
是随机变量y
落在范围[-x, x]
的概率。这里y
服从均值为0、方差为0.5的正态分布。由于它表示某种概率,补充误差函数1 - erf(x)
也经常使用。Owl中的误差函数及其相关变体列在[@tbl:maths:error]中。
函数 | 解释 |
---|---|
erf x |
误差函数 |
erfc x |
补充误差函数:\(1 - \textrm{erf}(x)\) |
erfcx x |
缩放的补充误差函数:\(\exp(x^2) \mathrm{erfc}(x)\) |
erfinv x |
误差函数的反函数 |
erfcinv x |
补充误差函数的反函数 |
误差函数是一个sigmoid函数。我们可以通过下面的代码观察其形状。
let _ =
let h = Plot.create "example_erf.png" in
Plot.(plot_fun ~h ~spec:[ RGB (66, 133, 244); LineStyle 1; LineWidth 2.] Maths.erf (-3.) 3.);
Plot.output h

积分函数
除了我们到目前为止提到的内容,Owl还提供了几个特殊积分函数。
例如,道森函数定义为:\[D(x) = e^{-x^2}\int_0^x~e^{t^2}dt\]
而菲涅尔三角函数积分返回一个包含两部分的元组:\[S(x) = \int_0^x~sin(t^2)dt, C(x) = \int_0^x~cos(t^2)dt.\]
与许多其他特殊函数一样,这两种类型的积分受到了物理研究的启发,如电磁问题。它们分别由dawsn
和fresnel
函数在Maths
模块中提供。我们可以通过绘图观察这些积分函数的函数。
let _ =
let h = Plot.create ~m:1 ~n:2 "example_integrals.png" in
Plot.subplot h 0 0;
Plot.(plot_fun ~h ~spec:[ RGB (66, 133, 244); LineStyle 1; LineWidth 2.] Maths.dawsn (-5.) 5.);
Plot.set_ylabel h "dawsn(x)";
Plot.subplot h 0 1;
Plot.(plot_fun ~h ~spec:[ RGB (66, 133, 244); LineStyle 1; LineWidth 2.] (fun x -> let s, _ = Maths.fresnel x in s) 0. 5.);
Plot.(plot_fun ~h ~spec:[ RGB (219, 68, 55); LineStyle 2; LineWidth 2.] (fun x -> let _, c = Maths.fresnel x in c) 0. 5.);
Plot.(legend_on h ~position:SouthEast [|"S(x)"; "C(x)"|]);
Plot.set_ylabel h "fresnel(x)";
Plot.output h

除了这两个之外,还提供了其他几种特殊积分函数。完整列表显示在[@tbl:maths:integral]中。
函数 | 解释 |
---|---|
expn n x |
广义指数积分 \(E_n(x) = x^{n-1}\int_x^{\infty}\frac{e^{-t}}{t^n}dt\) |
shi x |
双曲正弦积分:\(\int_0^x~\frac{\sinh~t}{t}dt\) |
chi x |
双曲余弦积分:\(\gamma + \log(x) + \int_0^x~\frac{\cosh~t -1}{t}dt\) |
shichi x |
(shi x , chi x ) |
si x |
正弦积分:\(\int_0^x~\frac{\sin~t}{t}dt\) |
ci x |
余弦积分:\(\gamma + \log(x) + \int_0^x~\frac{\cos~t -1}{t}dt\) |
sici x |
(si x , ci x ) |
阶乘
在函数之后,让我们转向一个我们熟悉的概念:阶乘。阶乘的定义很简单:
\(F(n) = n! = n \times (n - 1) \times (n-2) \ldots \times 1\)
阶乘函数及其几个变体都包含在Math
模块中。
函数 | 解释 |
---|---|
fact n |
阶乘函数\(!n\) |
log_fact n |
阶乘函数的对数 |
doublefact n |
双阶乘函数计算\(n!! = n(n-2)(n-4)\dots 2\)(或 1) |
log_doublefact n |
双阶乘函数的对数 |
阶乘函数接受整数作为输入,例如:
Maths.fact 5
>- : float = 120.
阶乘在数学的许多领域中都有应用,尤其是在组合学中。排列和组合都以阶乘的形式定义。排列函数返回有序子集的数量\(n!/(n-k)!\),长度为\(k\),从包含\(n\)个元素的集合中取出。组合函数返回集合\(n\)个元素中\(k\)个元素的子集数量,即\({n\choose k} = n!/(k!(n-k)!)\)。表[@tbl:maths:perm]提供了您可以在Math
模块中使用的组合数学函数。
函数 | 解释 |
---|---|
permutation n k |
排列数 |
permutation_float n k |
类似于permutation ,但处理更大范围并返回浮点数 |
combination n k |
组合数 |
combination_float n k |
类似于combination ,但处理更大范围并返回浮点数 |
log_combination n k |
返回\({n\choose k}\)的对数 |
让我们看一个简单的例子。
let x = Maths.combination 10 2
>val x : int = 45
let y = Maths.combination_float 10 2
>val y : float = 45.
插值和外推
有时我们不知道函数\(f\)的完整描述,只知道其中一些点,因此无法计算其在任意点的值。我们的目标是通过在给定数据点之间绘制平滑曲线来估计\(f(x)\)。如果\(x\)在给定数据的范围内,这个任务称为插值,否则称为外推,后者要困难得多。
Owl_maths_interpolate
模块提供了一个用于插值和外推的polint
函数:
val polint : float array -> float array -> float -> float * float
函数polint xs ys x
对给定的数组xs
和ys
执行多项式插值。给定数组\(xs[0 \ldots (n-1)]\)和\(ys[0\ldots~(n-1)]\),以及一个值x
,此函数返回一个值y
和一个误差估计dy
。
顾名思义,polint
使用可能通过给定点的最低可能次数的多项式来近似复杂的曲线。我们通过一个例子展示这种插值方法的工作原理。在前一节中,我们已经说过Gamma函数实际上是整数函数\(y(x) = (n-1)!\)的插值解。因此,我们可以指定平面上由该阶乘函数生成的五个节点,并查看插值函数与Gamma函数本身相比的效果。
let x = [|2; 3; 4; 5; 6|]
>val x : int array = [|2; 3; 4; 5; 6|]
let y = Array.map (fun x -> Maths.fact (x - 1)) x
>val y : float array = [|1.; 2.; 6.; 24.; 120.|]
let x = Array.map float_of_int x
>val x : float array = [|2.; 3.; 4.; 5.; 6.|]
积分
我们已经介绍了一些特殊的积分函数,但我们仍然需要适用于任何输入函数的一般积分方法。给定一个函数\(f\),它接受一个实变量和实轴上的一个区间\([a, b]\),该函数的积分
\[\int_a^bf(x)dx\]
可以被视为笛卡尔平面上由曲线\(f\)界定、在\([a, b]\)范围内的区域的带符号面积之和。位于x轴上方的区域增加到总和中,而位于x轴下方的区域则从总面积中减去。
在Owl_maths_quandrature
模块中,Owl提供了几种数值例程,以帮助您进行积分。例如,我们可以使用以下代码计算\(\int_1^4x^2\):
Owl_maths_quadrature.trapz (fun x -> x ** 2.) 1. 4.
>- : float = 21.0001344681758439
我们可以使用微积分基本定理验证这个结果:
\[\int_1^4x^2 = (4^3 -1^3) / 3 = 21\]。
所以您可能会想,这个trapz
是什么?为什么结果不是确切的21
?使用数值方法(或数值积分)进行积分可以追溯到微积分的发明甚至更早。基本思想是使用小面积的总和来近似积分的面积。存在许多算法用于数值积分,使用梯形规则是其中之一。
这个经典方法将a
到b
划分为\(N\)个等间距的横坐标:\(x_0, x_1, \ldots, x_N\)。在\(x_i\)和\(x_j\)之间的每个区域被看作一个“梯形”,并且面积公式计算为:
\[\int_{x_0}^{x_1}f(x)dx = h(\frac{f(x_0)}{2} + \frac{f(x_1)}{2}) + O(h^3f'').\]
这里的误差项\(O(h^3f'')\)表明近似误差与横坐标大小\(h\)和原始函数的二阶导数有关。函数trapz
实现了这种方法。它的接口是:
val trapz : ?n:int -> ?eps:float -> (float -> float) -> float -> float -> float
trapz ~n ~eps f a b
使用梯形法计算f
在区间[a,b]
上的积分。它通过迭代多个阶段工作,每个阶段通过添加更多的内部点来提高准确性。参数\(n\)指定最大步数,默认为20,eps
是所需的分数精度阈值,默认为1e-6
。
其他方法在接口上类似于trapz
,只是在实现上有所不同。例如,simpson
使用辛普森公式:
\[\int_{x_0}^{x_2}f(x)dx = h(\frac{f(x_0)}{3} + \frac{4f(x_1)}{3} + \frac{f(x_2)}{3}) + O(h^5f(4)).\]
然后有龙贝格积分(romberg
)可以选择不同阶数的方法以提供良好的准确性,这些算法通常比trapz
和simpson
方法快得多。此外,如果横坐标可以变化,我们有自适应的固定容差高斯积分(gaussian
)和固定阶数的高斯积分(gaussian_fixed
)。
例如,我们可以使用数值积分方法计算上一节中的特殊正弦积分函数\(Si(x)=\int_0^x\frac{sin(t)}{t}dt\)。让我们设定\(x=4\)。我们可以看到数值方法gaussian
很好地逼近了这个特殊积分函数。
实用函数
除了我们到目前为止提到的内容之外,还有一些值得一提的实用数学函数。
我们知道,质数是大于1
的自然数,不能通过两个较小的自然数相乘而得到。这是信息技术中的一个关键概念,广泛应用于公钥密码学等应用。函数is_prime
检查整数是否为质数。该函数对所有能由整数表示的数字都是确定性的。它是使用Miller-Rabin素性测试方法实现的。
Maths.is_prime 997
>- : bool = true
另一个与数论相关的概念是费马因子分解,它将奇整数表示为两个平方的差:\(N = a^2 - b^2\),因此N
可以分解为\((a+b)(a-b)\)。函数fermat_fact
对奇数N
执行费马因子分解,即分解为两个大致相等的因子\(x\)和\(y\),使得\(N=x\times~y\)。
Maths.fermat_fact 6557
>- : int * int = (83, 79)
83 * 79
>- : int = 6557
接下来的两个函数涉及计算机中浮点数的精度。
nextafter from to
返回from
在to
方向上的下一个可表示的双精度值。另一个函数nextafter from to
返回from
在to
方向上的下一个可表示的单精度值。在两种情况下,如果from
等于to
,则返回此值本身。例如:
Maths.nextafterf 1. 2.;;
>- : float = 1.00000011920928955
Maths.nextafter 1. 2.;;
>- : float = 1.00000000000000022
Maths.nextafter 1. 0.;;
>- : float = 0.999999999999999889
总结
我们从我们熟悉的数学函数开始讨论数值计算的主题。本章介绍了 Owl 支持的数学函数,包括基本和常用函数、一些不太常用但仍然重要的特殊函数、阶乘、插值、外推、积分等。随时跳转到您感兴趣的任何部分,并使用这些函数解决手头的数学问题。您可能会发现 Owl 与任何其他数值库一样优秀的计算器。