集合§

Raku 包含 Set 数据类型,以及对 大多数集合运算 的支持。 并集和交集 不仅是原生运算,而且使用其自然符号 ∩ 和 ∪。例如,此代码将检查有限数量集合的集合算术的基本定律

my @arbitrary-numbers = ^100;
my \U = @arbitrary-numbers.Set;
 
my @sets;
 
@sets.push: Set.new@arbitrary-numbers.pick@arbitrary-numbers.elems.rand)) for @arbitrary-numbers;
 
my (@union@intersection);
 
for @sets -> $set {
    @union.push: $set  $set === $set;
    @intersection.push: $set  $set === $set;
}
 
say "Idempotent union is "so @union.all;
# OUTPUT: «Idempotent union is True␤» 
say "Idempotent intersection is "so @intersection.all;
# OUTPUT: «Idempotent intersection is True␤» 
my (@universe@empty-set@id-universe@id-empty);
 
for @sets -> \A {
    @universe.push: A  U === U;
    @id-universe.push: A  U === A;
    @empty-set.push: A  ∅ === ∅;
    @id-empty.push: A  ∅ === A;
}
 
say "Universe dominates "so @universe.all;    # OUTPUT: «Universe dominates True␤» 
say "Empty set dominates "so @empty-set.all;  # OUTPUT: «Empty set dominates True␤» 
 
say "Identity with U "so @id-universe.all;    # OUTPUT: «Identity with U True␤» 
say "Identity with ∅ "so @id-empty.all;       # OUTPUT: «Identity with ∅ True» 

这段代码使用了 Raku 中已经定义的 空集,不仅检查了集合代数中的等式是否成立,还通过 无符号变量 和集合运算符的 Unicode 形式,使用尽可能接近原始形式的表达式;例如,A ∪ U === U,除了使用 值恒等运算符 === 之外,非常接近于 维基百科条目 中的实际数学表达式。

我们甚至可以测试德摩根定律,如下面的代码所示

my @alphabet = 'a'..'z';
my \U = @alphabet.Set;
sub postfix:<>(Set $a{ U  $a }
my @sets;
@sets.push: Set.new@alphabet.pick@alphabet.elems.rand)) for @alphabet;
my ($de-Morgan1,$de-Morgan2= (True,True);
for @sets X @sets -> (\A, \B){
    $de-Morgan1 &&= (A  B)⁻  === A⁻  B⁻;
    $de-Morgan2 &&= (A  B)⁻  === A⁻  B⁻;
}
say "1st De Morgan is "$de-Morgan1;
say "2nd De Morgan is "$de-Morgan2;

我们声明 补集运算,它计算全集 U 和我们的集合之间的对称差 ⊖。一旦声明了它,就可以相对容易地用非常接近原始数学符号的符号来表达诸如 A 和 B 的并集的补集 (A ∪ B)⁻ 之类的运算。

算术§

Raku 可以使用不同的数据类型进行算术运算。 NumRatComplex 都可以在加、减、乘和除运算下作为 进行运算(从技术上讲,需要注意的是,处理浮点数表示的数据类型在数学意义上不是域,因为它们的算术运算存在固有的不精确性。但是,对于大多数情况来说,它们构成了足够接近的、对计算机友好的此类数学对象的版本)。等效的数学域是

Raku 类
Rat
Num
Complex

整数 (Int),或者在数学中通常称为 ℤ,不是数学域,而是环,因为它们在乘法逆元下不封闭。但是,如果使用整数除法 div,它们的运算将始终产生其他整数;另一方面,如果使用 /,一般来说,结果将是 Rat

此外,Int 可以进行无限精度算术(或者至少是内存允许的无限精度;数值溢出仍然可能发生),而不会在数字过大时回退到 Num

my @powers = 22 ** * ... Infsay @powers[4].chars# OUTPUT: «19729␤»

严格来说,行为类似于数学域的有理数类是 FatRat。出于效率原因,使用 Rat 进行运算将在数字足够大或分子和分母之间存在较大差异时回退到 NumFatRat 可以使用任意精度,与默认的 Int 类相同。

生态系统中的一些模块可以使用其他数据类型进行数学运算

数字会自动根据它们实际表示的数字类进行鸭子类型化

.^name.say for (4, ⅗, 1e-93+.1i); # OUTPUT: «Int␤Rat␤Num␤Complex␤»

算术运算通过考虑操作数的类型来执行

say .33-.22-.11 == 0# OUTPUT: «True␤»

在这种情况下,所有数字都被解释为 Rat,这使得运算精确。一般来说,大多数其他语言会将它们解释为浮点数,如果需要,Raku 也可以实现这一点

say .33.Num -.22.Num - .11.Num# OUTPUT: «1.3877787807814457e-17␤»

对于这种情况,Raku 还包含一个近似相等运算符,

say .33.Num -.22.Num - .11.Num  0# OUTPUT: «True␤»

序列§

序列 是一个枚举的集合,其中允许重复,它也是 Raku 中称为 Seq 的一等数据类型。 Seq 能够表示无限序列,例如自然数

my \𝕟 = 1,2 … ∞;
say 𝕟[3];         # OUTPUT: «4␤»

无限序列使用 ∞、Inf*(Whatever)作为终止符。 是列表生成器,实际上只要插入第一个数字,它就可以理解算术和几何级数序列

say 1,5,9 … * > 100;
# OUTPUT: «(1 5 9 13 17 21 25 29 33 37 41 45 49 53 57 61 65 69 73 77 81 85 89 93 97 101)␤» 
say 1,3,9 … * > 337# OUTPUT: «(1 3 9 27 81 243 729)␤»

第一个序列将在生成数字大于 100 时终止;第二个序列是等比数列,将在它大于 337 时终止。

可以使用任意生成器这一事实使得生成诸如 斐波那契数列 之类的序列变得容易。

say 1,1* + * … * > 50;#  OUTPUT: «(1 1 2 3 5 8 13 21 34 55)␤»

事实上,我们可以用这种方法计算 黄金分割 的近似值。

my @phis = (2.FatRat1 + 1 / * ... *);
my @otherphi = (1 - @phis[200], 1 + 1 / * ... *);
say @otherphi[^10|(2030 ... 100)];  # OUTPUT: 
# «((-0.61803398874989484820458683436563811772030918 
# -0.61803398874989484820458683436563811772030918 
# -0.61803398874989484820458683436563811772030918 
# -0.61803398874989484820458683436563811772030918 
# -0.61803398874989484820458683436563811772030918 
# -0.618033…»

Math::Sequences 模块包含许多数学序列,已经为您定义好了。它包含许多来自 百科全书 的序列,其中一些带有其原始名称,例如 ℤ。

一些集合运算符也作用于序列,它们可以用来找出某个对象是否是序列的一部分。

say 876  (7,14 … * > 1000) ; # OUTPUT: «False␤»

在这种特殊情况下,我们可以直接找出 876 是否是 7 的倍数,但同样的原理也适用于使用复杂生成器的其他序列。我们也可以使用集合包含运算符。

say (55,89).Set  (1,1* + * … * > 200); # OUTPUT: «True␤»

也就是说,它没有考虑它是否实际上是一个子序列,只是这里两个元素的存在。集合没有顺序,即使您没有显式地将子序列转换为集合或显式地将它转换为 Seq,它也会在应用包含运算符时被强制转换为集合。

数学常数§

Raku 包含一组数学常数。

say π# OUTPUT: «3.141592653589793␤» 
say τ# Equivalent to 2π; OUTPUT: «6.283185307179586␤» 
say 𝑒; # OUTPUT: «2.718281828459045␤»

这些常数也可以通过 ASCII 等价物 获得:epitau

Math::Constants 模块包含一系列额外的物理和数学常数,例如前面提到的黄金分割 φ 或普朗克常数 ℎ。

由于 Raku 允许定义使用 Unicode 字符的变量,以及没有任何符号的变量和常量名称,因此在尽可能的情况下使用概念的实际数学名称来命名它们被认为是一种良好的做法。

常微分方程的数值积分§

Raku 是一种很棒的编程语言,你可以用它做很多很酷的数学运算。应用数学家在工作中要做的很多工作是模拟他们创建的模型。因此,在每种编码语言中,数值积分都是必不可少的。学习如何在 Raku 中做到这一点非常有用。

需求§

在 Raku 中,生态系统中有一些模块可以使它更容易。

  • Math::Model 允许您以简单自然的方式编写数学和物理模型。

  • Math::RungeKutta 用于常微分方程组的龙格-库塔积分。

对于这个例子,我们将使用 Math::Model 因为它有用的语法,但请记住,这个模块也需要 Math::RungeKutta。在使用这些示例之前,只需使用 zef 安装它们即可。

马尔萨斯模型§

让我们从数学生态学的“Hello World”开始:马尔萨斯增长模型。马尔萨斯增长模型,有时被称为简单指数增长模型,本质上是基于函数与其增长速度成正比的思想的指数增长。然后,方程如下所示

dx/dt = g*x

x(0) = x_0

其中 g 是人口增长率,有时被称为马尔萨斯参数。

我们如何将其转换为 Raku?好吧,Math::Model 以一种非常容易理解的方式提供了一些帮助。

use Math::Model;
 
my $m = Math::Model.new(
    derivatives => {
        velocity => 'x',
    },
    variables   => {
        velocity           => { $:growth_constant * $:x },
        growth_constant    => { 1 }# basal growth rate 
    },
    initials    => {
        x       => 3,
    },
    captures    => ('x'),
);
 
$m.integrate(:from(0), :to(8), :min-resolution(0.5));
$m.render-svg('population growth malthus.svg':title('population growth'));

为了完全理解正在发生的事情,让我们一步一步地进行。

分步解释§

  • 首先,我们加载用于进行计算的模块:Math::Model

    use Math::Model;
  • 我们创建一个模型,将所有信息都放入其中。

    my $m = Math::Model.new(
  • 我们声明模型中的导数。在本例中,如果你还记得我们的方程式,我们有变量x及其导数x'(通常称为速度)。

    derivatives => {
        velocity => 'x',
    },
  • 之后,我们声明模型如何演变。我们只需要为不是积分变量的导数(在本例中,只有x)和公式中使用的其他变量(增长率)提供公式。

    variables   => {
        velocity           => { $:growth_constant * $:x},
        growth_constant    => { 1 }# basal growth rate 
    },
  • 最后,我们声明初始条件,并使用captures告诉Math::Model在模拟运行期间记录哪些变量。

    initials    => {
        x       => 3,
    },
    captures    => ('x'),

此时,我们的模型已设置完毕。我们需要运行模拟并渲染一个关于结果的酷炫图表。

$m.integrate(:from(0), :to(8), :min-resolution(0.5));
$m.render-svg('population growth malthus.svg':title('population growth'));

在这里,我们选择时间限制和分辨率。接下来,我们生成一个图表。都明白了吗?好吧,让我们看看结果吧!

链接到图像

看起来不错!但说实话,它不太具有代表性。让我们探索来自更复杂情况的其他示例!

Logistic 模型§

资源不是无限的,我们的种群也不会永远增长。P-F Verhulst 也这么认为,所以他提出了Logistic 模型。该模型是种群增长的常见模型,其中繁殖率与现有种群和可用资源的数量成正比,其他条件相同。它看起来像这样

dx/dt = g*x*(1-x/k)

x(0)=x_0

其中常数 g 定义增长率,k 是承载能力。修改上面的代码,我们可以模拟它随时间的行为。

use Math::Model;
 
my $m = Math::Model.new(
    derivatives => {
        velocity => 'x',
    },
    variables   => {
        velocity           => { $:growth_constant * $:x - $:growth_constant * $:x * $:x / $:k },
        growth_constant    => { 1 },   # basal growth rate 
        k                  => { 100 }# carrying capacity 
    },
    initials    => {
        x       => 3,
    },
    captures    => ('x'),
);
 
$m.integrate(:from(0), :to(8), :min-resolution(0.5));
$m.render-svg('population growth logistic.svg':title('population growth'));

让我们看看我们酷炫的图表:链接到图像

如你所见,种群增长到最大值。

强 Allee 效应§

很有趣,不是吗?即使这些方程式看起来很简单,它们也与我们世界中的许多行为有关,例如肿瘤生长。但是,在结束之前,让我向你展示一个奇怪的案例。Logistic 模型可能是准确的,但是...当种群规模从某个阈值开始变得很小,以至于由于个体无法找到其他个体而导致生存率和/或繁殖率下降时会发生什么?

好吧,这是一个由 W.C. Allee 描述的有趣现象,通常称为Allee 效应。获得与这种效应相关的不同行为的一种简单方法是使用 Logistic 模型作为出发点,添加一些项,并得到一个像这样的三次增长模型

dx/dt=r*x*(x/a-1)*(1-x/k)

其中所有常数与之前相同,A 称为临界点

我们的代码将是

use Math::Model;
 
my $m = Math::Model.new(
    derivatives => {
        velocity_x    => 'x',
    },
    variables   => {
        velocity_x           => { $:growth_constant * $:x *($:x/$:a -1)*(1$:x/$:k},
        growth_constant    => { 0.7 },   # basal growth rate 
        k                  => { 100 }# carrying capacity 
        a                  => { 15 },  # critical point 
    },
    initials    => {
        x       => 15,
    },
    captures    => ('x'),
);
 
$m.integrate(:from(0), :to(100), :min-resolution(0.5));
$m.render-svg('population growth allee.svg':title('population growth'));

尝试自己执行此代码,看看当你改变临界点周围的初始条件时出现的不同行为!

弱 Allee 效应§

虽然强 Allee 效应是具有临界种群规模或密度的种群 Allee 效应,但弱 Allee 效应是没有任何临界种群规模或密度的种群 Allee 效应,即,表现出弱 Allee 效应的种群在较低的种群密度或规模下将具有降低的每 capita 增长率。但是,即使在如此低的种群规模或密度下,种群也始终会表现出正的每 capita 增长率。该模型与强模型略有不同,得到一个新的公式

dx/dt=r*x*(1-x/k)*(x/a)**n,其中 n>0

我们的代码将是

use Math::Model;
 
my $m = Math::Model.new(
    derivatives => {
        velocity_x => 'x',
    },
    variables   => {
        velocity_x           => { $:growth_constant * $:x *(1$:x/$:k)*($:x/$:a)**$:n },
        growth_constant    => { 0.7 },   # basal growth rate 
        k                  => { 100 }# carrying capacity 
        a                  => { 15 },  # critical point 
        n                  => { 4  }
    },
    initials    => {
        x       => 15,
    },
    captures    => ('x'),
);
 
$m.integrate(:from(0), :to(100), :min-resolution(0.5));
$m.render-svg('population growth allee.svg':title('population growth'));

额外信息§

你喜欢物理吗?查看模块创建者 Moritz Lenz 的原始帖子:https://perlgeek.de/blog-en/perl-6/physical-modelling.html