Perl 我的斐波那契序列作为递归函数是一个无限循环
下面的函数无限递归,我不明白为什么。它输入条件语句,但似乎不会以Perl 我的斐波那契序列作为递归函数是一个无限循环,perl,recursion,fibonacci,Perl,Recursion,Fibonacci,下面的函数无限递归,我不明白为什么。它输入条件语句,但似乎不会以return语句终止 use strict; use warnings; print fibonacci(100); sub fibonacci { my $number = shift; if ($number == 0) { print "return 0\n"; return 0; } elsif ($number == 1) { prin
return
语句终止
use strict;
use warnings;
print fibonacci(100);
sub fibonacci {
my $number = shift;
if ($number == 0) {
print "return 0\n";
return 0;
}
elsif ($number == 1) {
print "return 1\n";
return 1;
}
else {
return fibonacci($number-1) + fibonacci($number-2);
}
}
您的循环不是无限递归的,它只是在输入为100的情况下花费了太长的时间。尝试一个记忆版本:
{ my @fib;
sub fib {
my $n = shift;
return $fib[$n] if defined $fib[$n];
return $fib[$n] = $n if $n < 2;
$fib[$n] = fib($n-1) + fib($n-2);
}
}
{my@fib;
次级纤维{
我的$n=班次;
如果定义了$fib[$n],则返回$fib[$n];
如果$n<2,则返回$fib[$n]=$n;
$fib[$n]=fib($n-1)+fib($n-2);
}
}
通过记忆代码,可以使代码更加简洁,并大大加快速度
该模块是迄今为止记忆(缓存)子例程结果的最简单、最清晰的方法
我建议这个版本。它仍然警告深层递归,但它生成一个我认为是正确的值
use strict;
use warnings;
use Memoize;
memoize('fibonacci');
print fibonacci(100);
sub fibonacci {
my ($n) = @_;
$n < 2 ? $n : fibonacci($n-1) + fibonacci($n-2);
}
递归函数调用不是无限的。这需要很长时间 应该注意递归函数,因为如果可以这样设计的话,通常最好使用循环来编写代码 在这种情况下,对
fibonacci
的递归调用数呈指数增长。通过简单地将较低级别的调用相加,您可以很容易地测试这一点
=1次调用fibonacci(0)
=1次呼叫fibonacci(1)
=1次调用+调用fibonacci(2)
+调用fibonacci(1)
→ 三,fibonacci(0)
=1次呼叫+呼叫fibonacci(3)
+呼叫fibonacci(2)
→ 五,fibonacci(1)
=1次呼叫+呼叫fibonacci(4)
+呼叫fibonacci(3)
→ 九,fibonacci(2)
=1次呼叫+呼叫fibonacci(5)
+呼叫fibonacci(4)
→ 十五fibonacci(3)
=1次调用+调用fibonacci(N)
+调用fibonacci(N-1)
→ ?李>fibonacci(N-2)
fibonacci(100)
的调用次数,我们只需创建一个快速的perl one liner:
$ perl -e '
@c = (1, 1);
$c[$_] = 1 + $c[$_-1] + $c[$_-2] for (2..100);
print $c[100]
'
1.14629568802763e+21
以每秒1万亿次的速度,这仍然需要31年多的时间才能完成
使用Memoize修复
已经提出的一个修复方法是使用核心库
此模块将围绕您的子例程调用并缓存值的计算。因此,这将使函数调用的数量从1.14e21减少到101次计算
但是,由于递归次数超过100次,因此每次都会收到以下警告
- 匿名子例程的深度递归
- 子例程“%s”上的深度递归 (W递归)此子例程(直接或间接)调用自身的次数是其返回次数的100倍。这可能表示无限递归,除非您正在编写奇怪的基准测试程序,在这种情况下,它表示其他内容 通过重新编译perl二进制文件,将C预处理器宏perl_SUB_DEPTH_WARN设置为所需值,可以将该阈值从100更改为100
use strict;
use warnings;
print fibonacci(100), "\n";
sub fibonacci {
my $number = shift;
my @fib = ( 0, 1 );
for ( $#fib + 1 .. $number ) {
$fib[$_] = $fib[ $_ - 1 ] + $fib[ $_ - 2 ];
}
return $fib[$number];
}
产出:
3.54224848179262e+20
不会给出任何警告。尝试一个小得多的数字作为输入。它不是无限循环;这需要很长的时间(并且可能会触发检测无限循环的检查,因为它运行堆栈的深度)。如果您正在玩代码高尔夫,重写函数体是可以的,但在其他方面是不必要的。只需在OP的定义上使用
memoize
,它就可以加速到在我的机器上不到15毫秒的时间内返回第100个斐波那契数。@MarkReed:这远远不是代码高尔夫。所有必要的空白都已准备就绪,以明确其含义。如果您对条件运算符感到不舒服,那么我表示同情,但是习惯于阅读Perl代码的人会发现它比14行原始代码更容易阅读;我只是说,性能提升不需要改变。:)@马克·里德:不,马克,你说这个变化“如果你在打代码高尔夫的话没问题,但在其他方面是不必要的”。这与“性能提升不需要”不同。在我的机器上,这种手动记忆的性能比Memoize
的性能高出2倍,尽管对于使用模块的自动化和一致性有一些说法。@MarkReed:我怀疑你是否会发现很多人关心他们是在15毫秒还是7毫秒内得到结果。@MarkReed Memoize使用哈希实现缓存,因为它涵盖了一般情况。在这个特定示例中使用数组可能是更高性能的来源。
use strict;
use warnings;
print fibonacci(100), "\n";
sub fibonacci {
my $number = shift;
my @fib = ( 0, 1 );
for ( $#fib + 1 .. $number ) {
$fib[$_] = $fib[ $_ - 1 ] + $fib[ $_ - 2 ];
}
return $fib[$number];
}
3.54224848179262e+20