我正在尝试通过计算公式来计算e常数(AKA欧拉数)
为了一口气计算阶乘和除法,我这样写:
my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
say reduce * + * , @e[^10];
但是没有成功。如何正确执行?
我在分析您的代码部分中分析您的代码。在此之前,我介绍了一些有趣的奖金材料部分。
say e; # 2.718281828459045
单击上面的链接,查看达米安·康威(Russian Conway)在Raku中有关计算e
的非凡文章。
这篇文章很有趣(毕竟是达米安)。这是关于计算e
的非常容易理解的讨论。这是对拉里(Larry Wall)倡导的TIMTOWTDI哲学的碳酸氢盐转世的致敬。[[3
鉴于这些有效的方法都以相同的方式工作-通过累加一系列无限项(的初始子集),如果我们有一个函数可以更好地做到这一点,那就更好了。如果函数能够准确地自己算出该序列的初始子集实际多少以产生准确的答案,那肯定会更好……而不是要求我们手动梳理函数的结果。多次试验发现了这一点。而且,就像在Raku中一样,构建我们所需的东西非常容易:
sub Σ (Unary $block --> Numeric) {
(0..∞).map($block).produce(&[+]).&converge
}
分析您的代码这是第一行,生成系列:
my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
闭包({ code goes here }
)计算一项。闭包具有一个隐式或显式的签名,该签名确定它将接受多少个参数。在这种情况下,没有显式签名。$_
(the "topic" variable)的使用导致隐式签名,该签名需要一个绑定到$_
的参数。[序列运算符(
...
)反复调用其左侧的闭包,将前一项作为闭包的参数传递给lazily,以构建一系列术语,直到其右侧的端点为*
],是Inf
又称无穷大的简写。第一次调用闭包的主题是
1
。因此,闭包计算并返回1 / (1 * 1)
,得出系列的前两个项为1, 1/1
。second调用中的主题是上一个
下一个闭包将计算为1/1
的值,即再次是1
。因此,闭包计算并返回1 / (1 * 2)
,将序列扩展到1, 1/1, 1/2
。一切看起来都不错。1 / (1/2 * 3)
的0.666667
。该术语应为1 / (1 * 2 * 3)
。糟糕。使您的代码与公式匹配
您的代码应符合以下公式:
在此公式中,每个术语都是基于序列中的
位置
来计算的。系列中的第[[k个项(第一个1
的k = 0)只是阶乘k的倒数。(因此,与上一项的value无关。因此,接收到前一项的value
的$_
不应在闭包中使用。)] >让我们创建一个阶乘后缀运算符:sub postfix:<!> (\k) { [×] 1 .. k }
([
×
是一个中缀乘法运算符,比通常的ASCII中缀Unicode alias好看*
。]
这是简写:
sub postfix:<!> (\k) { 1 × 2 × 3 × .... × k }
((我在花括号内使用了伪元语法符号来表示根据需要添加或减去任意多个项的想法。
[通常,在表达式的开头将中缀运算符op
放在方括号中,将形成一个与reduce with => &[op],
等效的复合前缀运算符。有关更多信息,请参见Reduction metaoperator。现在我们可以重写闭包以使用新的阶乘后缀运算符:
my @e = 1, { state $a=1; 1 / $a++! } ... *;
宾果。这将产生正确的系列。
...直到没有,因为另一个原因。下一个问题是数值精度。但让我们在下一部分中进行处理。从您的代码派生的一个班轮
也许将三行压缩为一:
say [+] .[^10] given 1, { 1 / [×] 1 .. ++$ } ... Inf
.[^10]
适用于由given
设置的主题。 (^10
是0..9
的简写,因此上面的代码计算序列中前十个项的和。)
我已经从闭项中删除了$a
,计算下一项。孤独的$
与匿名状态标量(state $)
相同。通过将$a
初始化为1
,我将其设置为增量递增而不是增量递减,以达到与您相同的效果。现在剩下最后一个(大!)问题,您在下面的评论中指出。
[其操作数都不是
Num
(浮点数,因此是近似值),Num
运算符通常返回100%准确的/
(有限精度的有理数)。但是,如果结果的分母超过64位,则该结果将转换为Rat
-以性能换取准确性,这是我们不想做的折衷。我们需要考虑到这一点。要指定
无限精度
以及100%的精度,只需强制操作即可使用Rat
。要正确执行此操作,只需(至少)使一个操作数为Num
(其他都不为FatRat
):我已经将此数字验证为500个十进制数字。我希望它能够保持准确,直到程序由于超出Raku语言或Rakudo编译器的某些限制而崩溃为止。 (有关此说明,请参见
FatRat
FatRat
。)
脚注1
Raku具有一些重要的内置数学常数,包括Num
,say [+] .[^500] given 1, { 1.FatRat / [×] 1 .. ++$ } ... Inf
和my answer to Cannot unbox 65536 bit wide bigint into native integer(及其别名e
)。因此,人们可以像在数学书中那样,在Raku中写出Euler的身份。归功于i
:
pi
2达米安的文章是必读的。但这只是
π
的100多次比赛中令人钦佩的治疗之一。3
有关由python爱好者编写的TIMTOWTDI的较平衡视图之一,请参见RosettaCode's Raku entry for Euler's Identity。但是,将TIMTOWTDI拖得太远了。为了反映后者的“危险”,Perl社区创造了幽默长,不易理解且低估的内容# There's an invisible character between <> and iπ character pairs!
sub infix:<> (\left, \right) is tighter(&infix:<**>) { left * right };
# Raku doesn't have built in symbolic math so use approximate equal
say e**iπ + 1 ≅ 0; # True
-有多种方法可以实现,但有时一致性也不是坏事,发音为“ Tim Toady Bicarbonate” 。 google for 'raku "euler's number"',Larry将碳酸氢盐应用于Raku的设计,而Damian将其应用于Raku的计算TIMTOWTDI vs TSBO-APOO-OWTDI。 TIMTOWTDIBSCINABTE中有分数。因此,您需要Strangely enough或更确切地说是e
。