zoukankan      html  css  js  c++  java
  • 轻言“算法”太匆匆——也谈“兔子”面试题

        园子里面试题目盛行,今天看到一个关于“兔子”数量的问题,回复很多,但感觉都不得要领,答案也是千奇百怪、五花八门。这里给出我的一个写法。

    题目:
        一对小兔子一年后长成大兔子;一对大兔子每半年生一对小兔子。大兔子的繁殖期为4年,兔子的寿命是6年。假定第一年年初投放了一对小兔子,试编程计算,第n年末总共会有多少对兔子。n由键盘输入。(用递归哦,谢谢!)(http://www.cnblogs.com/zuozuo/archive/2011/09/18/2180426.html

    1.理解问题
        每次看到这种题目,我总是胆战心惊,担心自己把题目理解错。自然语言中的“一年后”、“每半年”、“繁殖期为4年”、“寿命是6年”以及“第n年末”其实一不小心就会弄错,而一旦弄错,结果必然谬之千里。
        编程的首要原则是什么?我认为是理解问题(Understand the problem first!参见:http://programmers.stackexchange.com/questions/91527/what-do-you-consider-the-1st-principles-of-programming)。在考虑算法之前,必须理解要解决的问题是什么。如果连问题都不清楚,怎么可能谈得上算法呢?
        很多人一旦拿到题目,就立刻认为自己已经理解了问题。然而对于我来说,我宁愿花几倍的时间来理解问题。下面就是我对题目的理解过程:
        首先我假设一个具体的起始时间(人笨,只好用笨办法):2000年1月1日。从这天起有了最初的那1对小兔子(并且假设它们为0岁。实际上还可以是半岁,这一点题目并未明确,应该算是题目本身的一个漏洞)。由于“大兔子每半年生一对小兔子”且“兔子的寿命是6年”,6年是半年的整数倍数,所以兔子的数量每半年变化一次。因此每半年考察一次兔子的数目。

                          0岁   半岁(+) 1岁 1.5岁 2岁 2.5岁 3岁 3.5岁 4岁 4.5岁 5岁 5.5岁    (总)
    00年01月01日      1                                                                                          1 
    00年07月01日             1                                                                                   1 第1年末
    01年01月01日      1               1                                                                         2 
    01年07月01日      1     1               1                                                                  3 第2年末                            
    02年01月01日      2     1        1            1                                                            5            
    02年07月01日      3     2        1     1            1                                                     8  第3年末
    03年01月01日      5     3        2     1     1          1                                               13
    03年07月01日      8     5        3     2     1     1           1                                        21 第4年末
    04年01月01日    13     8        5     3     2     1    1             1                                 34             
    04年07月01日    21    13       8     5     3     2     1     1             1                          55 第5年末
    05年01月01日    33    21      13    8     5     3     2     1       1             1                  88             
    05年07月01日    54    33      21   13    8     5     3     2       1      1            1           142 第6年末
    06年01月01日    86    54      33   21   13     8    5     3       2      1      1                 227           
    06年07月01日  139    86      54   33   21   13     8     5       3      2      1    1           366 第7年末
    (注:用EXCELL做这个表极其方便)

        由于“兔子的寿命是6年”,所以不可能有6周岁的兔子
        由于“繁殖期为4年”,所以只有1~4.5岁的兔子有生育能力
        特别要注意的是,“第n年末”的含义则意味着经历了2*n-1次繁殖。所谓第7年年末就是06年7月1日到06年12月31日之间的数量。这个期间兔子数量没有变化。

    2.代码
        根据前面的分析,这个题目的算法很简单,几乎没有研讨的必要。重要的是建立适当的数据结构
        用一个数组来表示各年龄兔子的数目即可:
        unsigned rabbits [12];
        其中,rabbits[0]表示0岁兔子数目,rabbits[1]表示半岁兔子数目……,由于不可能有6周岁的兔子,所以数组只需要12个数据。
        每过半年,兔子长半岁。意味着数组各个元素依次向后移动一个位置。最后一个数据,由于不存在超过6周岁的兔子,所以舍弃。每次移动后根据rabbits[2]~rabbits[9](繁殖期为4年)的总数计算新出生的兔子数目(rabbits[0])。
        计算第n年末兔子的数目,用一个简单的2*n-1次循环就可以完成,这实在谈不上什么算法。不过既然题目要求递归,那么好吧

    /*
    题目:
    一对小兔子一年后长成大兔子;一对大兔子每半年生一对小兔子。
    大兔子的繁殖期为4年,兔子的寿命是6年。
    假定第一年年初投放了一对小兔子,试编程计算,第n年末总共会有多少对兔子。
    n由键盘输入。(用递归哦,谢谢!)
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #define COUNT_IN_HALF(years) ( 2 * ( years )  ) 
    
    #define GROW_UP              ( COUNT_IN_HALF(1) )//一对小兔子一年后长成大兔子
    #define REPRODUCTION_BEGIN   ( GROW_UP )
    #define REPRODUCTION_END     ( GROW_UP + COUNT_IN_HALF(4) )//大兔子的繁殖期为4年
    #define LIFE_END             ( COUNT_IN_HALF(6) ) //兔子的寿命是6年
    
    #define SIZE(a)              ( sizeof(a)/sizeof(*a) )
              
    unsigned breed ( unsigned [], size_t , unsigned );
    unsigned sum   ( unsigned [], size_t );
    void     move  ( unsigned [], size_t );
    
    int main( void )
    {
      unsigned  rabbits [LIFE_END]   //不存在满6周岁的兔子
                = { 1  } ;             
      unsigned n ;
      
      printf("即算第几年末的兔子数?\n");
      scanf("%u",&n);
                
      printf( "%u\n" , 
              breed ( rabbits , SIZE( rabbits ) , COUNT_IN_HALF( n )  ) 
             );
        
      system("PAUSE");	
      return 0;
    }
    
    unsigned breed ( unsigned rabbits[] , size_t size , unsigned half_years )
    {
       
       if( half_years == 1 )          // half_years - 1 次 
         return sum( rabbits , size );
       
       move( rabbits , size - 1 );    //兔子长半岁
    
       rabbits[0] = sum ( rabbits + GROW_UP , REPRODUCTION_END - GROW_UP  ); //繁殖
       
       return breed( rabbits , size , half_years - 1 );   
    
    }
    
    //索性也递归 
    unsigned sum ( unsigned rabbits[] , size_t size )
    {
       if( size == 0) 
          return 0 ;
    
       return sum( rabbits , size - 1 ) + rabbits[size - 1];
    }
    
    //将递归进行到底 ,呵呵 
    void move  (  unsigned rabbits[] , size_t i  )
    {
       if( i == 0 )
         return ;
       rabbits[ i ]=rabbits[ i-1 ];  
       move ( rabbits , i - 1 );
    }
    

       感想:1.先看懂题。2.良好的数据结构往往不需要什么复杂的算法。

        顺便说一下,这个面试题目基本和下面那道题目基本完全一样

    http://www.cnblogs.com/KBTiller/archive/2011/07/15/2107161.html#2175878

  • 相关阅读:
    C#调用存储过程的几个方法
    easyui 获取焦点
    easyui datagrid 添加统计行
    js 序列化form
    post 加返回判断
    启动journalnode时出现问题
    安装zookeeper集群及出现的问题
    win7旗舰版(64位)环境下oracle11g的安装方法(转)
    jdbc连接各种数据库方式列表
    在Windows平台上使用XManager 3.0连接Redhat 企业版 5和6.
  • 原文地址:https://www.cnblogs.com/KBTiller/p/2181672.html
Copyright © 2011-2022 走看看