同事提出一个问题,关于牛生小牛的算法,之前的牛生小牛的算法都是假设牛不死的情况下的,几年生一头,若干年后还有多少头,这个算法是有些变化的。
农场第一年有一头牛,假设这头牛每2年生一次小牛,小牛迅速长大,2年后又能生小牛,但是牛活到5岁的时候回自然的死去,那么求第N年还有多少牛。
我想,这个问题的难点在于,牛要死去。自然而然的,这里要进行设置一个条件,当牛的年龄达到5岁,让牛死去(就是让这个元素消失)。同事基于这种思路,写出了第一版本的代码
按照题意逻辑理解,进行顺序逻辑判断,编程代码如下
<?php $fun=function($n){ $list=[1]; while ($n--) { foreach ($list as $k => $v) { if($v%2==0) { $list[]=1;//生一头小牛 } if($v==5) { unset($list[$k]);//到五岁了 牛死了 continue; } $list[$k]++;//牛长大了一岁 } } return count($list);//返回牛的数目 }; echo $fun(10); exit();
这个代码逻辑非常清晰,是基础中的典范,然而,我们发现当年数增加的时候,内存会溢出,比如在求100年以后的时候
然后他提出了改进方案使用array_pop回收
截图如下
代码如下:
<?php $fun1 = function($n){ $list = [1]; while($n--){ foreach($list as $k => $v){ if($v%2 == 0){ $list[] = 1; } if($v == 5){ //unset($list[$k]); $list[$k] = array_pop($list); continue; } $list[$k]++; } } return count($list); }; echo $fun1(100); exit();
然而这样还是会爆出内存泄漏 而且多了一个notice
针对牛的死去,以及再生,我通过牛的数量寻找规律想到了一个方案,不知道能不能被理解。
截图如下:
代码如下:
<?php $sum=get_cow_num(10); var_dump($sum); exit(); function get_cow_num($n) { $sum=1; for ($i=1; $i < $n; $i++) { $sum = $i%2!=0 ? $sum*2 : $sum-peibona($i/2-1);//奇数年要翻倍 偶数年减去斐波那契数 } return $sum; } function peibona($n) { if($n<=0){return 0;} if($n<=2){return 1;} return peibona($n-1)+peibona($n-2); }
在计算100年以后的时候 30秒时间不足
我想办法延长时间
结果……
十分钟过去了,程序仍然没有停止的意思,真的是够够的了,看来递归不太适合,我猜测最后会因为递归,报错提示PHP不能递归超过99层,因为之前出过这样的问题。
那么我改进一下,采用曾经解决过的斐波那算法来解决这个问题。
这牛数量也太多了吧……
代码如下:
<?php set_time_limit(0); $sum=get_cow_num(100); var_dump($sum); exit(); function get_cow_num($n) { $sum=1; for ($i=1; $i < $n; $i++) { $sum = $i%2!=0 ? $sum*2 : $sum-peibona($i/2-1);//奇数年要翻倍 偶数年减去斐波那契数 } return $sum; } function peibona($n) { if($n<=0){return 0;} if($n<=2){return 1;} $array=array_fill(0,$n,0); $array[0]=0; $array[1]=1; $array[2]=1; for ($i=3; $i <= $n; $i++) { $array[$i]=$array[$i-1]+$array[$i-2]; } return $array[$n]; }
这这段代码有没有优化的地方呢?当然有我们发现每次算斐波那切数都是要循环重置进行计算,为何不用静态缓存呢?
代码如下:
<?php set_time_limit(0); $sum=get_cow_num(100); var_dump($sum); exit(); function get_cow_num($n) { $sum=1; for ($i=1; $i < $n; $i++) { $sum = $i%2!=0 ? $sum*2 : $sum-peibona($i/2-1,$n/2-1);//奇数年要翻倍 偶数年减去斐波那契数 } return $sum; } function peibona($n,$total_num) { static $array; if(!isset($array[$n])) { if($n<=0) { $array[$n]=0; }elseif($n<=2) { $array[$n]=1; } else { $array=array_fill(0,$n,0); $array[0]=0; $array[1]=1; $array[2]=1; for ($i=3; $i <= $n; $i++) { $array[$i]=$array[$i-1]+$array[$i-2]; } } } return $array[$n]; }
这代码,哈哈哈哈,风骚的让人无法理解啊!
后来根据同事总结规律得出公式,采用偶数翻倍,奇数减去斐波那切数的公式,得到python代码
这代码极其风骚,采用递归完成,不过我估计不用递归应该更快才对。
关于斐波那切数的通项公式可以参考百度百科 https://baike.baidu.com/item/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97/99145?fr=aladdin