gpt4 book ai didi

code-generation - 使用Raku计算e数

转载 作者:行者123 更新时间:2023-12-04 10:13:51 25 4
gpt4 key购买 nike

我正在尝试通过计算公式来计算e常数(又称为Euler数)


为了一口气计算出阶乘和除法,我这样写:

my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
say reduce * + * , @e[^10];


但这没有解决。如何正确做?

最佳答案

我将在分析您的代码部分中分析您的代码。在此之前,我介绍了一些有趣的红利材料部分。
一线一字1

say e; # 2.718281828459045

"A treatise on multiple ways" 2
单击上面的链接以查看Damian Conway在Raku中有关计算 e的非凡文章。
这篇文章很有趣(毕竟是达米安)。这是关于计算 e的非常容易理解的讨论。这是对Raku对拉里·沃尔(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
第二个呼叫中的主题是前一个呼叫 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的倒数。
(因此,它与前一项的值无关。因此,接收前项值的 $_不应在闭包中使用。)
让我们创建一个阶乘后缀运算符:
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设置的主题。 ( ^100..9的简写,因此上面的代码计算序列中前十个项的和。)
我已经从下一个计算的闭包中删除了 $a。孤独的 $与匿名状态标量 (state $)相同。通过将 $a初始化为 1,我将其设置为增量递增,而不是增量递减,以达到与您相同的效果。
您现在在下面的评论中指出了最后一个(大!)问题。
如果两个操作数都不是 Num(浮点数,因此是近似值),则 /运算符通常返回100%准确的 Rat(有限精度的有理数)。但是,如果结果的分母超过64位,则该结果将转换为 Num -以性能换取准确性,这是我们不希望进行的折衷。我们需要考虑到这一点。
要指定无限制的精度以及100%的精度,只需强制操作使用 FatRat。为正确执行此操作,只需(至少)使一个操作数为 FatRat(其他都不为 Num):
say [+] .[^500] given 1, { 1.FatRat / [×] 1 .. ++$ } ... Inf

我已将此值验证为500个十进制数字。我希望它能保持准确,直到程序由于超出Raku语言或Rakudo编译器的某些限制而崩溃为止。 (有关此说明,请参见 my answer to Cannot unbox 65536 bit wide bigint into native integer。)
脚注
1 Raku内置了一些重要的数学常数,包括 eipi(及其别名 π)。因此,人们可以像在数学书中那样,在Raku中写出Euler的身份。归功于 RosettaCode's Raku entry for Euler's Identity
# 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

2 Damian的文章是必读的。但这只是 google for 'raku "euler's number"'的100多个匹配项中的几种令人钦佩的处理之一。
3有关由python爱好者编写的TIMTOWTDI的更为平衡的视图之一,请参见 TIMTOWTDI vs TSBO-APOO-OWTDI。但是,将TIMTOWTDI放得太远也有弊端。为了反映后者的“危险”,Perl社区创造了幽默长,难以理解且被低估的 TIMTOWTDIBSCINABTE-有多种方法可以实现,但有时一致性也不是坏事,发音为“ Tim Toady Bicarbonate” 。 Strangely enough,Larry将碳酸氢盐应用于Raku的设计,而Damian将其应用于Raku的计算 e

关于code-generation - 使用Raku计算e数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59794623/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com