C - 如何跟踪此递归?

时间:2015-11-19 00:00:31

标签: c recursion

我一直在寻找在线递归(在C中)的例子,试图更好地理解它以及它是如何工作的。一般来说,我可以在没有问题的情况下跟踪一些基本的递归问题(例如因子问题),但是我找到了这个并且完全失去了如何跟踪它。

这个想法是让用户输入一定量的更改,并通过使用递归,打印出可以进行更改的方式的数量。代码如下:

#include <stdio.h>

#define NUM_DENOMS 4

int ways(int amount, int denomination);

int main()
{
    //Declarations & initializations
    int userChange = 0;

    //Get user input
    printf("Enter an amount you wish to get change for (in cents):\n");// get the amount of change in from the user
    scanf("%d", &userChange);

    //Function call... pass user's input and denomination values (ints) as parameters
    printf("%d cents can be made in %d different ways\n", userChange, ways(userChange, NUM_DENOMS));

    return 0;
}

//Function to find # of ways to make change for user's amount
int ways(int amount, int denomination)
{
    static int validAmounts[NUM_DENOMS] = {1, 5, 10, 25};

    if(denomination<=0) //if denomination is invalid
    {
        return 0;
    }
    if((amount == 1) || (amount == 0)) //base case: only 1 combination
    {
        return 1;
    }
    else if(amount < 0) //can't have negative amount of money
    {
        return 0;
    }
    else //if denomination is valid and user's change > 1
    {
        return ways(amount, denomination-1) + ways(amount-validAmounts[denomination-1], denomination);
    }
}

显然这是递归的常见应用。我无法理解这种递归是如何工作的。对我来说最突出的是在同一行上有2个递归调用。我从未见过以这种方式应用的递归。

我确实试图追踪它但我的结果肯定是错误的:

假设我输入25作为更改量。当我进入ways函数时,没有任何基本情况得到满足,因此递归发挥作用。对于第一次调用,amount保持不变,denomination减少1,所以我们回到函数中,使用25和3(4-1)作为新参数。在denomination减少为0之前,不会满足任何基本情况(因为amount永远不会更改)。在这一点上,我们返回0.这是我迷路的地方。我看到0通过所有之前的调用被发回,因此最终结果是0,但这对我来说听起来不对。我试图跟踪第二个调用时遇到了同样的问题,除了通过调用发回的0,它是1.显然我对这种递归的看法是非常错误的。有人可以向我解释这个递归实例是如何工作的吗?

2 个答案:

答案 0 :(得分:1)

跟踪递归算法的一种方法是将printf放在递归函数的顶部。 printf应该打印出函数的参数。暂时添加更多参数以提供有关递归正在执行的操作的其他信息也是一个好主意。最常见的附加参数是depth参数,该参数显示已进行了多少次嵌套调用。对于这个特殊的问题(你有两个递归调用)我会添加一个额外的参数来识别正在跟踪哪个调用。

考虑到这一点,这里是修改后的代码。我建议从一个简单的输入开始,比如5,以了解递归的工作原理。

#include <stdio.h>

#define NUM_DENOMS 4

int ways(int amount, int denomination, int left, int depth);

int main( void )
{
    int userChange = 0;

    printf("Enter an amount you wish to get change for (in cents):\n");
    scanf("%d", &userChange);

    printf("%d cents can be made in %d different ways\n", userChange, ways(userChange, NUM_DENOMS, 'M', 0));

    return 0;
}

int ways(int amount, int denomination, int left, int depth)
{
    static int validAmounts[NUM_DENOMS] = {1, 5, 10, 25};

    printf( "%2d %d %c %2d\n", amount, denomination, left, depth );

    if(denomination <= 0 || amount < 0)
        return 0;

    if((amount == 1) || (amount == 0))
        return 1;

    return ways(amount, denomination-1, 'L', depth+1) + ways(amount-validAmounts[denomination-1], denomination, 'R', depth+1);
}

答案 1 :(得分:0)

代码进行两次调用,因为它将问题分成两部分,每个部分以相同的方式解决。在某种意义上,每个部分都比原始问题简单,并且使用相同的方法来解决每个单独的问题。正如其他人所指出的,可能存在两个以上的情况。

您可能已经看到一个调用的示例,其中问题的一部分已解决,并且单个递归调用解决了剩余的问题&#39;问题。