我试图使用Ramanujan算法近似pi :

它应该计算总和,直到最后一个总和小于1e-15.
这应该只是为了好玩而占用我最多半小时的时间......但我的代码并没有产生任何接近pi的东西,我不知道为什么.很可能是我忽略的傻事,但不确定!
只是一个注释:我从$k1 开始,因为0打破了我的factorialsub,从我的计算中k = 0无论如何都会返回0.
我意识到代码可以更有效地编写; 我尽可能简单地写出来,看看能不能理解我哪里出错了.任何帮助赞赏!
#!/usr/bin/perl
use warnings;
use strict;
sub approx_pi {
my $const = (2 * sqrt(2)) / 9801;
my $k = 1;
my $sum = 0;
while ($sum < 1e-15) {
my $p1 = factorial((4 * $k), 1);
my $p2 = 1103 + (26390 * $k);
my $p3 = (factorial($k, 1))**4;
my $p4 = 396**(4 * $k);
$sum = $sum + ( ($p1 * $p2) / ($p3 * $p4) );
$k++;
}
#print "Const: $const\nSum: $sum\n";
return (1 / ($const * $sum));
}
sub factorial {
my ($i, $total) = @_;
return $total if $i == 1;
$total = $total * $i;
#print "i: $i total: $total\n";
factorial($i-1, $total);
}
my $pi = approx_pi();
print "my pi is: $pi\n";
Run Code Online (Sandbox Code Playgroud)
Tru*_*ueY 13
更新
脚本有几个问题.
k==0那么项目是1103.所以$k从0 开始,而不是1.为此你应该修改factorial:
sub factorial {
my ($i, $total) = @_;
return $total if $i <= 1;
Run Code Online (Sandbox Code Playgroud)
sub fact {
my $n = shift;
return 1 if $n == 0 || $n ==1;
return $n * fact($n -1);
}
Run Code Online (Sandbox Code Playgroud)
(请参阅Mark Reed关于原始脚本中可能的尾调优化问题的有趣评论.在此答案的最后更多关于它.)
$sum值应该小于阈值,而是第k个差异项.所以在approx_pi你应该使用这样的东西:
my $Diff = 1;
while ($Diff > 1e-15) {
my $p1 = factorial((4 * $k), 1);
my $p2 = 1103 + (26390 * $k);
my $p3 = (factorial($k, 1))**4;
my $p4 = 396**(4 * $k);
$Diff = ($p1 * $p2) / ($p3 * $p4);
$sum += $Diff;
$k++;
}
Run Code Online (Sandbox Code Playgroud)
factorial和计算它396 power of 4k是无效的,所以它们可以只是停止.
sub approx_pi {
my $const = 4900.5 / sqrt(2);
my $k = 0;
my $k4 = 0;
my $F1 = 1;
my $F4 = 1;
my $Pd = 396**4;
my $P2 = 1103;
my $P4 = 1;
my $sum = 0;
while (1) {
my $Diff = ($F4 * $P2) / ($F1**4 * $P4);
$sum += $Diff;
last if $Diff < 1e-15;
++$k;
$k4 += 4;
$F1 *= $k;
$F4 *= ($k4 - 3)*($k4 - 2)*($k4 - 1)*$k4;
$P2 += 26390;
$P4 *= $Pd;
}
return $const / $sum;
}
Run Code Online (Sandbox Code Playgroud)
结果是:
my pi is: 3.14159265358979
Run Code Online (Sandbox Code Playgroud)
我做了一些措施.Approx_pi功能运行了1 000 000次.固定的原始版本需要24秒,另一个需要5秒.对我来说,有点有趣的是,$F1**4比...更快$F1*$F1*$F1*$F1.
关于阶乘的一些话.由于Mark的评论,我尝试了不同的实现,以找到最快的解决方案.为不同的实现运行了5 000 000个循环来计算15!:
sub rfact;
sub rfact($) {
return 1 if $_[0] < 2;
return $_[0] * rfact $_[0] - 1 ;
}
Run Code Online (Sandbox Code Playgroud)
46.39秒
sub lfact($) {
my $f = 1;
for(my $i = 2; $i <= $_[0]; ++$i) { $f *= $i }
return $f;
}
Run Code Online (Sandbox Code Playgroud)
16.29秒
_fact 15,1):
sub _fact($$) {
return $_[1] if $_[0] < 2;
@_ = ($_[0] - 1, $_[0] * $_[1]);
goto &_fact;
}
Run Code Online (Sandbox Code Playgroud)
108.15秒
my @h = (1, 1);
sub hfact;
sub hfact($) {
return $h[$_[0]] if $_[0] <= $#h;
return $h[$_[0]] = $_[0] * hfact $_[0] - 1;
}
Run Code Online (Sandbox Code Playgroud)
3.87秒
my @h = (1, 1);
sub hlfact($) {
if ($_[0] > $#h) {
my $f = $h[-1];
for (my $i = $#h + 1; $i <= $_[0]; ++$i) { $h[$i] = $f *= $i }
}
return $h[$_[0]];
}
Run Code Online (Sandbox Code Playgroud)