我的工作是为此方法编写递归版本。根据我的理解,Recursion从一个基本调用开始(如果有东西然后返回),然后是一个展开回原始基础的else。就像从甲板开始一样,加入甲板然后从甲板上取下卡片,直到你回到原来的甲板上。 考虑到这一点。
public static long fact(int n)
{
long result = 1;
while(n > 0)
{
result = result * n;
n = n - 1;
}
return result;
}
//我的递归版本:
public static void recFact(int n)
{
if(n==0)
{
return n; // ir 0 it really doesn't matter right?
}
else
{
return recFact(n-1);
}
}
这只是我出现的考试的一个示例测试问题,只是想确保我有一个递归的句柄。我做对了吗?如果不是我错过了什么?请不要回答问题的答案,只要告诉我我做错了什么,也许就更好的理解方法提出一些建议。
感谢。
答案 0 :(得分:3)
您的递归版本没有乘法,并且对于任何输入它将返回零。所以不,你做得不对。
但是,递归版本会递归,所以你有这个想法!要了解出了什么问题,请完成一个非常简单的案例。
客户来电recFact(3)
这将返回给客户recFact(2)
哪个将返回到recFact(1)
以上
哪个将返回到recFact(0)
以上
哪个将返回到0
以上。
出现两个主要问题:
不希望解决方案交给你的态度很好!希望这些指针能帮助你解决这个问题。
编辑:显然我误解了你的语法,你确实想要解决方案。答案 1 :(得分:3)
不,这种递归解决方案不正确。
对于每个积极n
,您只需返回rectFact(n-1)
,这将追索到0
,此时它将返回。换句话说,您的函数将始终返回0
。您错过了乘以当前n
与rectFact(n-1)
的部分。另请注意,0!
为1,而不是0:
public static int recFact(int n)
{
if(n==0)
{
return 1;
}
else
{
return n * recFact(n-1);
}
}
最后,由于if
子句返回,else
有点多余。当然,这并不影响方法的正确性,但恕我直言,没有它,代码看起来更清晰:
public static int recFact(int n)
{
if(n==0)
{
return 1;
}
return n * recFact(n-1);
}
答案 2 :(得分:2)
任何递归函数都需要三件事:
终止条件:这告诉函数何时停止调用自身。这对于避免无限递归和避免堆栈溢出异常非常重要。
实际处理:您需要在每个功能中运行实际处理。在非递归的情况下,这是result = result * n
。 您的递归版本中缺少此内容!
收集器/聚合器变量:您需要一些方法来存储您下面的递归调用的部分结果。因此,您需要一些方法来返回recFact
的结果,以便您可以将其包含在调用链中更高的处理中。请注意,您说return recFact(n - 1)
但在定义recFact
中会返回void
。这应该是int
。
答案 3 :(得分:1)
根据您的示例,您遗漏了return type
的{{1}} recFact
同样int
将始终返回0,因为您每次都不会将recFact
乘以方法的递归调用。
答案 4 :(得分:0)
有两种方法可以编写递归例程。一个是"标准"我们都被教导的方式。这是一个入口点,必须首先检查递归链是否在末尾(escape子句)。如果是这样,它将返回"链的结尾"值并结束递归。如果不是最后,它会执行根据级别获取部分值所需的任何计算,然后调用自身将值传递给接近链末尾的下一个增量。
private final int InitialValue = 15;
System.out.println( "Fact(" + InitialValue + ") = " + recFact( InitialValue ) );
public int recFact( int val ){
if( val < 2 ){
return 1;
}
else{
return recFact( val - 1 ) * val; // recursive call
}
}
//Output: "Fact(15) = 2004310016"
在常规递归中,在每个级别维持部分答案,用于补充下一级别的答案。在上面的代码中,部分答案是val
。首次调用时,此值为15
。它取这个值并乘以Fact(14)的答案,为Fact(15)提供完整的答案。事实(14)得到了答案,将14乘以从事实(13)得到的答案,等等。
还有另一种称为尾递归的递归。这不同之处在于部分答案被传递到下一级而不是在每个级别维护。这听起来很复杂,但实际上,使递归过程更加简单。另一个区别是有两个例程,一个是非递归的,并设置递归例程。这是为了只为想要查看(并且只需要查看)
的用户维护标准APIanswer = routine( parameter );
非递归例程提供了这个功能。它也是放置一次性代码(例如错误检查)的便利位置。请注意,在上面的标准例程中,如果用户传入的是-15而不是15,则例程可能会弹出。这意味着在生产代码中,必须进行这样的测试。但是每次进入例程时都会执行此测试,这意味着除了第一次之外,所有测试都将不必要地进行。此外,因为它必须返回一个整数值,它不能处理大于19的初始值,因为这将导致一个将溢出32位整数容器的值。
public static final int MaxFactorialSeq = 20;
private final int InitialValue = 15;
System.out.println( "Fact(" + InitialValue + ") = " + recFact( InitialValue ) );
public int recFact( int value ){
if( value < 0 || value > MaxFactorialSeq ){
throw new IllegalArgumentException(
"Factorial sequence value " + value + " is out of range." );
}
return recFact( value, 1 ); // initial invocation
}
private int recFact( int val, int acc ){
if( val < 2 ){
return acc;
}
else{
return recFact( val - 1, acc * val ); // recursive call
}
}
//Output: "Fact(15) = 2004310016"
请注意,公共入口点包含范围检查代码。这只执行一次,递归例程不必进行此检查。然后它使用初始&#34;种子&#34;来调用递归版本。 1。
递归例程和以前一样,检查它是否在链的末尾。如果是这样,它返回,而不是像以前那样返回1,但是此时的累加器具有完整的答案。然后,调用链将回退到非递归例程中的初始入口点。没有进一步的计算,因为答案是在向下计算而不是向上计算的。
如果你走过它,标准递归的答案是通过序列15 * 14 * 13 * ... * 2 * 1达到的。通过尾递归,答案是通过序列1 * 15 * 14 * ... * 3 * 2达到的。当然,最终的答案是一样的。但是,在初始值为15的测试中,标准递归方法平均为0.044毫秒,尾递归方法平均为0.030毫秒。但是,几乎所有的时间差都是由于我在标准递归例程中检查边界这一事实。没有它,时间更接近(0.036到0.030),但当然,你没有错误检查。
并非所有递归例程都可以使用尾递归。但是,并非所有的递归例程都应该如此。不言而喻,任何递归函数都可以使用循环编写。而且一般应该是。但是像上面那样的因子函数永远不会超过19个级别,所以它们可以被添加到幸运的少数人中。
答案 5 :(得分:-1)
递归的问题在于,要理解递归,首先必须理解递归。
递归函数是一个调用自身的函数,或者调用最终再次调用第一个函数的函数。
你有正确的递归部分,因为你的函数调用自己,你有一个&#34;转义&#34;因此你不会得到无限递归(函数不能调用自身的原因)。
你的例子中缺少的是你正在进行的实际操作。
此外,你需要传递你的计数器和你所乘的值,而不是传递一个计数器,然后你需要返回所说的乘法值。
public static long recFact(int n, long val)
{
if(n==1)
{
return val;
}
else
{
return recFact(n-1, val) * n;
}
}