母牛2年生小牛 5年后并死去的算法

同事提出一个问题,关于牛生小牛的算法,之前的牛生小牛的算法都是假设牛不死的情况下的,几年生一头,若干年后还有多少头,这个算法是有些变化的。

农场第一年有一头牛,假设这头牛每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

原文地址:https://www.cnblogs.com/lizhaoyao/p/8716512.html