返回声明有效,但没有多大意义

时间:2010-09-13 16:36:14

标签: c++ algorithm recursion analysis

我有以下功能:

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作为答案。

这是怎么发生的?我需要能够跟踪所发生的一切,以便我可以对其进行迭代以进行证明。

8 个答案:

答案 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;
}

尽管许多编译器会自动执行此简化,但在简化代码时,调试通常会更容易。单步调试时,调试器将匹配代码。

有时简化会增加清晰度。此外,添加评论将帮助您弄清楚您正在做什么,以及下一个阅读代码的人。