【问题标题】:Don't understand logic behind why this C++ recursion function works不理解为什么这个 C++ 递归函数起作用的逻辑
【发布时间】:2014-04-24 01:33:40
【问题描述】:

此函数旨在生成楼梯上大步和短步的组合数(用户给出的值)。小步幅为 1 步,大步幅为 2 步。

但是,我不明白这里使用的递归洞察力。我真的很感激解释为什么这会产生所需的组合数量。通过它,我可以看到它有效,但 我不确定我自己是如何得出这个逻辑的。

有人可以对此有所了解吗?

代码如下:

int CountWays(int numStairs);
int combination_strides = 0;
const int LARGE_STEP = 2;
const int SMALL_STEP = 1;

int main() {

    cout << "Enter the number of stairs you wish to climb: ";
    int response = GetInteger();
    int combinations = CountWays(response);
    cout << "The number of stride combinations is: " << combinations << endl;

    return 0;
}


int CountWays(int numStairs) {

    if (numStairs < 4) {
    return numStairs;

    } else {
    return CountWays(numStairs - SMALL_STEP) + CountWays(numStairs - LARGE_STEP);

    }
}

【问题讨论】:

    标签: c++ recursion


    【解决方案1】:

    要下numStairs,您可以:

    • 迈出一小步,然后往下走(numStairs - SMALL_STEP);或
    • 迈出一大步,然后往下走(numStairs - LARGE_STEP)

    所以总路数是下行路数(numStairs - SMALL_STEP)和下行路数(numStairs - LARGE_STEP)之和,因此是递归。

    很简单,可以看到有一种方法可以下一级 (S),有两种方法可以下 2 步(SS 或 L),三种方法可以下 3 步(SSS、SL 或 LS),因此终止条件。

    您可能会将此递归识别为Fibonacci sequence 的定义。对于奖励积分,您可能希望重新构建计算,使其以线性而非指数的时间运行。

    【讨论】:

    • 斐波那契数列比较绝对是我需要理解的提示。谢谢。
    【解决方案2】:

    好吧,让我们这样想。我还有很多楼梯。如果 numStars

    ss=small step (1)
    ls=large step (2)
    1 step-{{ss}}==1
    2 steps-{{ss,ss},{ls}}==2
    3 steps-{{ss,ss,ss},{ls,ss},{ss,ls}}==3
    

    比这更大,它可能会变得更复杂。但是,如果我还剩下 n,我可以迈出一大步,也可以迈出一小步。如果我迈出一大步,就会剩下numStars-LARGE_STEP个步骤,所以我只需要找到那么多步骤的组合总数。如果我迈出一小步,我只需要找到numStais-SMALL_STEP的组合总数。

    用递归的形式表示,combinations[n]=combinations[n-1]+combinations[n-2]。

    需要注意的是,无论如何这都不是最快的方法。这会为每次迭代重做大量工作。动态规划方法会更可取。

    int* num_combinations = new int[num_stairs];
    for (int i = 0; i < 4; i++) num_combinations[i]=i;
    for (int i = 4; i < num_stairs; i++) num_combinations[i]=num_combinations[i-1]+num_combinations[i-2];
    int ret = num_combinations[num_stairs-1]+num_combinations[num_stairs-2]
    delete[] num_conbinations;
    return ret;
    

    O(n) 与 O(n^2)

    【讨论】:

      【解决方案3】:

      这里是这种情况下的递归逻辑:

      假设您有N 步骤编号为1N

      在任何时候,您都可以选择以下两个选项之一:

      1. 上一步
      2. 爬两级

      你想计算爬上楼梯的不同组合的数量。

      现在,假设您目前正站在步骤K

      您可以选择爬到K+1K+2

      所以你需要做的就是添加:

      • 从阶梯K+1爬到阶梯N的不同组合的数量
      • 从阶梯K+2爬到阶梯N的不同组合的数量

      或者等效地,您可以添加:

      • 从楼梯底部爬到台阶的不同组合数N-(K+1)
      • 从楼梯底部爬到台阶的不同组合数N-(K+2)

      话虽如此,递归函数的工作原理如下:

      int CountWays(int numStairs)
      {
          if (numStairs < 4)
              return numStairs;
          // If there is  1 step , then there is  1 combination  to climb it   up
          // If there are 2 steps, then there are 2 combinations to climb them up
          // If there are 3 steps, then there are 3 combinations to climb them up
      
          int x = CountWays(numStairs - SMALL_STEP);
          // The number of different combinations to climb up
          // from the bottom of the staircase to step N-(K+1)
      
          int y = CountWays(numStairs - LARGE_STEP);
          // The number of different combinations to climb up
          // from the bottom of the staircase to step N-(K+2)
      
          return x+y;
      }
      

      您可能已经注意到,这个函数只产生斐波那契数列 1, 2, 3, 5, 8, 13, ...

      顺便说一句,在这种情况下迭代实现会更有效(就像在许多其他情况下一样):

      int CountWays(int numStairs)
      {
          int prev = 0;
          int curr = 1;
          for (int i=0; i<numStairs; i++)
          {
              int next = prev+curr;
              prev = curr;
              curr = next;
          }
          return prev;
      }
      

      【讨论】:

        【解决方案4】:

        代码试图通过在每一步中缩小问题来分解问题。 如果要走的台阶少于四级,则可能组合的数量等于楼梯的数量:
        - 对于一个楼梯,唯一可能的组合是一小步。
        - 对于两个楼梯,可能的组合是一个大台阶或两个小台阶。
        - 对于三个楼梯,您可以走三个小台阶、一个小台阶和一个大台阶或一个大台阶和一个小台阶。
        这些情况将结束递归。
        对于每其他数量的楼梯,您可以先走一小步,然后走其余的楼梯,或者走一大步,然后走其余的楼梯。 “楼梯的其余部分”是递归发生的地方:它只会计算减少楼梯数量的可能性数量(少一或两步,取决于第一步的大小)。
        将它们全部加在一起,就可以得到上楼梯的组合总数。

        【讨论】:

          【解决方案5】:

          设 f(n) 为从起点 (stair number 0) 到达第 n 个楼梯的方式数。

          要到达stair n,您首先必须到达起点n-1n-2
          同样要到达楼梯n-1,你需要到达n-2 th 或n-3 th stair,递归是这样的......

          但它必须在某个地方停止,否则你会继续评估 f(-1)f(-2) ,... f(- infinity) ... 所以有 stopping condition :要到达 stair 1 ,你只能这样做在1 步骤中,即直接从stair 0stair 1

          到达stair 2 , 2 ways : stair 0 + 步2 , 或stair 1 + 步1

          因此

          f(1) = 1  
          f(2) = 2  
          f(3) = 3  
          f(n) = f(n-1) + f(n-2) // n>3
          

          【讨论】:

            【解决方案6】:

            这个递归特别令人困惑的一点是,它是根据常量SMALL_STEPLARGE_STEP 参数化的,但终止条件(if (numsteps &lt; 4))只对@ 的特定值是正确的987654324@ 和 LARGE_STEP = 2 -- 如果您更改其中任何一个,则代码不正确。可以说代码应该是:

            int CountWays(int numStairs) {
                if (numStairs < 0) {
                    return 0;
                } else if (numStairs == 0) {
                    return 1;
                } else {
                    return CountWays(numStairs - SMALL_STEP) + CountWays(numStairs - LARGE_STEP);
                }
            }
            

            这效率稍低(需要几次额外的迭代才能得到答案),但仍然具有相同的渐近复杂度 -- O(2n)

            【讨论】:

            • 谢谢。我最初从 numStairs == 0 的基本情况开始,后来更改了它。当我使用您的解决方案时,它运行良好。当我删除了最初的 if (numStairs
            • 考虑到当剩余步数小于LARGE_STEP时,您不能从大步开始(因此尝试这样做会导致0种方式)
            • 这是有道理的。谢谢。
            猜你喜欢
            • 2021-10-28
            • 1970-01-01
            • 2020-04-02
            • 2017-07-13
            • 1970-01-01
            • 1970-01-01
            • 2013-11-02
            • 2018-05-31
            • 1970-01-01
            相关资源
            最近更新 更多