为什么这段代码的执行时间会超过30S

2013-12-25 03:46:31 +08:00
 kennedy32
function tuzi($x){
if($x == 0 || $x == 1){
return 1;
}else{
return tuzi($x-1)+tuzi($x-2);
}
}
function testtuzi($n){
for ($i=0;$i<=$n;$i++){
echo "兔子在".$i."月的数目是".tuzi($i)."<br/>";
}
}
testtuzi(60);
6371 次点击
所在节点    PHP
46 条回复
anheiyouxia
2013-12-25 11:10:04 +08:00
flyaway
2013-12-25 11:15:23 +08:00
斐波拉契数列,递归的方法存在太多的overlapping,大量重复计算,降低了效率
skydiver
2013-12-25 11:20:30 +08:00
@TMBest 不是说正确做法是直接用lgamma函数么……
skydiver
2013-12-25 11:39:23 +08:00
@skydiver 记错了。。那个是算阶乘。。
kennedy32
2013-12-25 11:39:58 +08:00
@Keita1314 额,我只看得懂php
kennedy32
2013-12-25 11:40:16 +08:00
@luoyou1014 谢谢
baiyunheitu
2013-12-25 12:05:18 +08:00
递归的复杂度不太乐观。
c742435
2013-12-25 12:14:16 +08:00
你用一个数组存储计算结果就可以了。算法不用改。
kuye
2013-12-25 12:35:18 +08:00
一种很肤浅的作法:
function tuzi($x){
static $result=array();
if(isset($result[$x])){
return $result[$x];
}
if($x == 0 || $x == 1){
$r= 1;
}else{
$r= tuzi($x-1)+tuzi($x-2);
}
$result[$x]=$r;
return $r;
}
function testtuzi($n){
for ($i=0;$i<=$n;$i++){
echo "兔子在".$i."月的数目是".tuzi($i)."<br/>";
}
}
testtuzi(60);
kevinroot
2013-12-25 13:29:24 +08:00
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
14896 root 25 0 302m 9.9m 6948 R 100.0 0.5 1:07.71 php tuzi.php

php tuzi.php 11870.42s user 4.86s system 99% cpu 3:18:38.25 total
才跑到46
dorentus
2013-12-25 14:53:49 +08:00
回答楼主的问题:

tuzi($x) 这个函数,里面用到了前两次的计算结果 tuzi($x-1) 和 tuzi($x-2)
但是,每次计算的结果并没有保存在什么地方,每次都是从头开始重新计算的

例如:要计算 tuzi(4),
>>>> 需要先计算 tuzi(3) 和 tuzi(2),分解为:
>>>>>>>> 计算 tuzi(3):需要先计算 tuzi(2) 和 tuzi(1)
>>>>>>>>>> 计算 tuzi(2):需要先计算 tuzi(1) 和 tuzi(0)
>>>>>>>>>>>> 计算 tuzi(1) 得 1
>>>>>>>>>>>> 计算 tuzi(0) 得 1
>>>>>>>>>> 计算 tuzi(1) 得 1
>>>>>>>> 计算 tuzi(2):需要先计算 tuzi(1) 和 tuzi(0)
>>>>>>>>>> 计算 tuzi(1) 得 1
>>>>>>>>>> 计算 tuzi(0) 得 1

注意上面每一步计算都是独立的(因为之前的结果并没有保存起来供以后使用),比如 tuzi(2) 就分别计算了两次。

然后,比如 testtuzi 的循环里面,下一步假如开始要计算 tuzi(5),需要计算 tuzi(4) 和 tuzi(3),然后虽然上次循环里面 tuzi(4) 和 tuzi(3) 其实已经都算过了,但是不行,这一次里面还是得重新从头开始算。

@kuye 的方法是拿空间换时间的方法(如果我对 PHP 的 static 没理解错的话),拿很少的空间(那个 static array)保存了每次 tuzi($x) 计算的结果,大大提高了后面计算的速度。
kennedy32
2013-12-25 16:49:14 +08:00
@dorentus 非常感谢,最详细的解答
kennedy32
2013-12-25 16:50:21 +08:00
@kuye 改动最小的方案,谢谢。其实这是MIT600SC第六课递归的例子,能用递归当然最好了
vibbow
2013-12-25 18:17:15 +08:00
能30s跑完这个代码的电脑都是神级的电脑了...
我的电脑还在慢慢跑,我看多久能跑完...
pljhonglu
2013-12-25 18:37:35 +08:00
其实 LZ 是来秀电脑配置的。。。
hourui
2013-12-25 19:25:18 +08:00
楼上正解...
kofj
2013-12-25 20:22:44 +08:00
@levn 怎么插入github的这种格式化了的代码的?
faceair
2013-12-25 20:36:04 +08:00
kurtis
2013-12-25 22:54:37 +08:00
@dorentus
看到现在,只有你got point

这个问题,本质就是应该拿空间换时间。
有张表记录下每次计算的结果作为缓存,以后不用每次计算就可以了。

这个是大学作业吗?好像很久很久以前有人讨论过这个问题了,
TheJuli
2013-12-25 23:01:31 +08:00
用超算平台跑一遍试试

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/94455

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX