为什么我在输出中获得额外的1 * 1并且它有点倒退?有点初学者有递归,会喜欢详细的答案。
class Program
{
public static long Factorial(int n)
{
if (n == 0)
return 1;
Console.WriteLine("{0} * {1}", n, Factorial(n - 1));
return n * Factorial(n - 1);
}
static void Main(string[] args)
{
long a = 0;
a = Factorial(3);
Console.WriteLine(a);
}
}
输出
1 * 1
2 * 1
1 * 1
3 * 2
1 * 1
2 * 1
1 * 1
6
答案 0 :(得分:4)
你在递归两次中调用函数,一次在输出中,然后再在下一行。这就是输出全部混乱的原因,因为您使用Console.WriteLine()
方法调用它,然后立即在return
语句中再次调用它。
此外,factorial为零是1,所以我在WriteLine()
方法中添加了一个小的三元语句检查,以便输出在数学上是正确的。
Console.WriteLine("{0} * {1}", n, Factorial(n - 1)); // Calling it once
return n * Factorial(n - 1); // Calling it again, oops!
这是一个有用的微调:
public static long Factorial(int n)
{
if (n == 0)
return 1;
Console.WriteLine("{0} * {1}", n, (n>1?n-1:n));
return n * Factorial(n - 1);
}
static void Main(string[] args)
{
long a = Factorial(3);
Console.WriteLine(a);
}
产量
3 * 2
2 * 1
1 * 1
6
答案 1 :(得分:1)
这是因为日志输出中有第二个因子循环:
Console.WriteLine("{0} * {1}", n, Factorial(n - 1));
这需要在打印之前计算Factorial(n-1)。 因为递归的每一步现在都会触发两次递归,所以它比你想象的要复杂得多!
So Factorial(3):
如果您将其更改为:
Console.WriteLine("{0} * Factorial({1})", n, n - 1);
然后你会得到更像你期望的日志记录。
(如果您正在学习递归,那么您可能会感兴趣的是使用调试器并查看程序的流程如何,以及为什么它会导致您看到的输出。)
答案 2 :(得分:0)
碰巧,你的错误与递归无关。你的问题是,你调用一个方法两次,而不是保存它的值。如果你不打印结果,你甚至不会意识到这一点。正如@JLK指出的那样,你在这段代码中称它为两次:
Console.WriteLine("{0} * {1}", n, Factorial(n - 1));
return n * Factorial(n - 1);
让我们逐步完成代码,以便我们理解输出:
Factorial (3);
-> output 3 * {Factorial (2)} // 3 * 2 (4)
-> output 2 * {Factorial (1)} // 2 * 1 (2)
-> output 1 * {Factorial (0)} // 1 * 1 (1)
<- 1
Factorial (0)
<- 2
Factorial (1)
-> output 1 * {Factorial (0)} // 1 * 1 (3)
<- 1
Factorial (0)
<- 1
<- 1
Factorial (2)
-> output 2 * {Factorial (1)} // 2 * 1 (6)
-> output 1 * {Factorial (0)} // 1 * 1 (5)
<- 1
Factorial (0)
<- 1
Factorial (1)
-> output 1 * {Factorial (0)} // 1 * 1 (7)
<- 1
Factorial (0)
<- 1
<- 2
这可能有点令人困惑,但这对于递归来说是正常的。所以你基本上所要做的就是稍微更改一下代码片段:
var factorial = Factorial (n - 1);
Console.WriteLine("{0} * {1}", n, factorial);
return n * factorial;
输出是:
1 * 1
2 * 1
3 * 2
6
如果您想了解有关递归的更多信息,我建议使用this网站。这不是关于C#,但它是一个学习递归的好网站。如果你想在递归中做很多事情,我不建议使用C#,因为它是一种必要的语言。对于递归,函数式语言更适合。如果您仍想接近C#,或者至少使用.Net框架,我建议使用F#,这是C#的功能等同。
C#和递归的主要问题是,C#不支持Tail recursion。当你进行更深层次的递归调用时(例如无限递归循环),这很容易导致StackOverflows。这是因为每次调用函数时,调用函数的代码地址都会被压入堆栈。因此,每次执行递归调用时,代码adddress都会被推送到堆栈。所以,如果你有这个功能:
void rec () => rec();
通过调用C#,您可以在不到一秒的时间内获得StackOverflow。然而,尾递归至少部分地解决了这个问题:如果递归调用是函数中的最后一次执行,它不会推送调用者的代码地址。所以在F#中,这将永远运行:
let rec r () =
r ()
r ()
(我不会深入研究F#语法,有足够的在线教程)。我说它只是部分地解决了这个问题,因为这个:
let rec r () =
r ()
()
r ()
再次产生StackOverflow。我不知道任何编程语言,但事实并非如此,所以你只需要习惯使用尾递归。
您可以在this帖子中阅读有关功能/递归语言的更多信息。
答案 3 :(得分:0)
你应该从记分牌上复制你的作业。你必须变成:
Console.WriteLine("{0} * {1}", n, Factorial(n - 1));
通过
Console.WriteLine("{0} * {1}", n, n - 1);
这个错误输出错误。