我有以下功能:
int mult(int y, int z)
{
if (z == 0)
return 0;
else if (z % 2 == 1)
return mult(2 * y, z / 2) + y;
else
return mult(2 * y, z / 2);
}
我需要做的是通过归纳证明其正确性。现在我遇到的麻烦是,即使我知道它运行起来,但我不能按照每个步骤进行操作。
让我感到困惑的是y
只显示为一个参数,除了在递归部分之外,它在任何地方都没有出现,但该函数实际上返回y
作为答案。
这是怎么发生的?我需要能够跟踪所发生的一切,以便我可以对其进行迭代以进行证明。
答案 0 :(得分:3)
因为这显然是一个家庭作业问题,所以我建议你做你所做的事情。跟踪代码。
1)给出y和z的起始值。 2)在纸上或在调试器中,跟踪调用函数时发生的情况。 3)使用当前的y / z值重复步骤2,直到程序完成。
答案 1 :(得分:2)
#include <iostream>
using namespace std;
int mult(int y, int z)
{
if(z==0) {
cout<<"z is null! - y:"<<y<<" z: "<<z<<endl;
return 0;
}
else if (z%2==1)
{
cout<<"z is odd! - y:"<<y<<" z: "<<z<<endl;
// make z even
return mult(2*y,z/2)+y;
}
else
{
cout<<"z is even! - y:"<<y<<" z: "<<z<<endl;
return mult(2*y,z/2);
}
}
int main() {
cout<<"result: "<<mult(3,13)<<endl;
}
输出:
z is odd! - y:3 z: 13
z is even! - y:6 z: 6
z is odd! - y:12 z: 3
z is odd! - y:24 z: 1
z is null! - y:48 z: 0
result: 39
它如何适用于3和13:
有偶数和奇数的开关(见代码注释)。
当z为null时,递归“开始返回初始调用”。如果数字z是奇数,则将y加到递归调用的返回值,如果它甚至只是返回递归调用的值。
odd: return 0 + 24
odd: return 24 + 12
even: return 36
odd: return 36 + 3
答案 2 :(得分:1)
注意:如果这是作业,请将其标记为。
所以,我们基本上得到了三个递归案例。为了使它更清楚,我将C代码重写为一些功能伪代码。将mult
替换为直观的操作符,并找出(z%2==1)
等低级表达式的描述性解释。
你会想出像
这样的东西a ** b =
| b is 0 -> 0
| b is even -> 2a ** (b/2)
| b is odd -> 2a ** (b/2) + a
你现在明白了吗?
答案 3 :(得分:1)
逐步分析
final result: 100
mult(10, 10)
{
makes 100
mult(20, 5)
{
makes 100
mult(40, 2) + 20
{
makes 80
mult(80, 1)
{
makes 80
mult(160, 0) + 80
{
return 0;
}
}
}
}
}
答案 4 :(得分:1)
一种方法是将每一行翻译成“英语”。我的翻译是这样的:
如果z为零,则返回零
如果z为奇数,则返回mult(y * 2,z / 2)+ y
如果z是偶数,则返回mult(y * 2,z / 2)
一般模式是递归调用mult,第一个参数加倍,第二个参数减半。
注意,这里你用z / 2调用mult,但是它的参数是整数,所以如果你的函数继续递归,第二个参数每次都会减半,直到它变为1,然后最后1/2向下舍入为0 - 此时递归将停止,因为z == 0。
通过这些线索,您应该能够理解该算法的工作原理。
答案 5 :(得分:0)
y a = y a
a =偶数
b =下一个奇数(换言之,a + 1)
所以,如果你想要上面的等式只有偶数('a')给定一个奇数('b')你可以做到以下几点:
y b = y (a + 1)= y * a + y
现在通过将'a'写为2 *(z / 2)来混淆每个人。
y * a变为(2 * y)*(z / 2)
y * b变为((2 * y)*(z / 2))+ y
由于'z'出现在偶数和奇数的公式中,我们想要认为代码告诉我们(2 * y)*(z / 2)=(2 * y)*(z / 2)+ y这显然是MADNESS!
原因是我们已经知道z / 2是一个整数,所以z永远不会是奇数。当z为奇数时,编译器不允许我们将z / 2赋值给整数。如果我们试图使'z'变为奇数,我们真正使用的整数是(z-1)/ 2而不是z / 2.
为了解决这个问题,我们必须测试z / 2是否为奇数并根据它选择我们的公式(例如,根据'a',y a或y b)
在mult(y,z)中,'y'和'z'都是整数。使用上面的符号mult(2 * y,b / 2)变为mult(2 * y,a / 2),因为编译器将b / 2截断为a / 2。
由于我们总是将'a'作为'mult'的参数,即使我们发送'b',我们也必须确保我们只使用需要'a'的公式。因此,如上所述,我们使用y a + 1而不是y b。
b / 2 = a / 2 + 1/2但1/2不能表示为int的一部分。
答案 6 :(得分:0)
通过归纳进行的演示基于证明结果对第一个值有效,并且如果原则对于通用值N
是正确的,则证明它适用于N+1
。
为简化起见,您可以首先证明它适用于z
{ 0, 1, 2 }
,这对于手动测试应该是微不足道的。然后,为了演示归纳步骤,您可以从通用z=N
开始,并证明如果mult( y, N )
是有效结果,那么mult( y, N+1 )
也是前一个有效的结果。由于偶数和奇数有不同的分支,因此您必须证明偶数和奇数N
数的归纳步骤。
答案 7 :(得分:0)
不是一个真正的答案,而是更多的建议 您可能希望将递归调用从2减少为1:
int mult(int y, int z)
{
int result = 0;
if (z == 0)
return result;
result = mult(2 * y, z / 2); // Common between "then" and "else"
if ((z % 2) == 1)
{
result += y;
}
return result;
}
通过遵守“仅一个出口点”规则,可以再次简化:
int mult(int y, int z)
{
int result = 0;
if (z != 0)
{
result = mult(2 * y, z / 2); // Common between "then" and "else"
if ((z % 2) == 1)
{
result += y;
}
}
return result;
}
尽管许多编译器会自动执行此简化,但在简化代码时,调试通常会更容易。单步调试时,调试器将匹配代码。
有时简化会增加清晰度。此外,添加评论将帮助您弄清楚您正在做什么,以及下一个阅读代码的人。