更新しました
スクリプトにはいくつかの問題があります。
その場合k==0
、アイテムは1103
です。したがって$k
、1 ではなく 0 から開始します。このためには、以下を変更する必要がありますfactorial
。
sub factorial {
my ($i, $total) = @_;
return $total if $i <= 1;
積を階乗で渡す必要はありません。それは次のようなものかもしれません:
sub fact {
my $n = shift;
return 1 if $n == 0 || $n ==1;
return $n * fact($n -1);
}
(元のスクリプトで発生する可能性のある末尾呼び出し最適化の問題に関する 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++;
}
ただし、常に再帰的に を呼び出して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;
}
結果は次のとおりです。
my pi is: 3.14159265358979
いくつかの対策を行いました。Approx_pi
関数は 1 000 000 回実行されました。修正された元のバージョンは 24 秒かかり、他のバージョンは 5 秒かかります。$F1**4
私にとって、が よりも速いことは、なんとなく興味深いことです$F1*$F1*$F1*$F1
。
階乗についてのいくつかの言葉。マークのコメントのため、最速の解決策を見つけるために、さまざまな実装を試しました。5 000 000 ループが実行され、さまざまな実装が計算されました15!
。
再帰バージョン
sub rfact;
sub rfact($) {
return 1 if $_[0] < 2;
return $_[0] * rfact $_[0] - 1 ;
}
46.39秒
シンプルなループバージョン
sub lfact($) {
my $f = 1;
for(my $i = 2; $i <= $_[0]; ++$i) { $f *= $i }
return $f;
}
16.29秒
コールテール最適化による再帰 (call _fact 15,1
):
sub _fact($$) {
return $_[1] if $_[0] < 2;
@_ = ($_[0] - 1, $_[0] * $_[1]);
goto &_fact;
}
108.15秒
中間値を格納する再帰
my @h = (1, 1);
sub hfact;
sub hfact($) {
return $h[$_[0]] if $_[0] <= $#h;
return $h[$_[0]] = $_[0] * hfact $_[0] - 1;
}
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]];
}