如何使用Master定理来描述递归?

时间:2008-10-20 17:27:10

标签: recursion recurrence master-theorem

最近我一直在研究递归;如何写它,分析它等等我曾经想过复发和递归是一回事,但是最近的家庭作业和测验中的一些问题让我觉得有一些细微的差别,“复发”是通往描述递归程序或函数。

直到最近,当我意识到有一些叫做“主要定理”的东西用来为问题或程序写出“重现”时,这对我来说都是非常希腊的。我一直在阅读维基百科页面,但是,像往常一样,事情的措辞是这样的,我并不真正理解它在谈论什么。我通过实例了解得更多。

所以,有几个问题: 让我们说你再次发生这种情况:

  

r(n)= 2 * r(n-2)+ r(n-1);
  r(1)= r(2)   = 1

事实上,这是主定理的形式吗?如果是这样,用语言来说,它是什么意思?如果你试图根据这种重复尝试编写一个小程序或一个递归树,那会是什么样子?我是否应该尝试替换数字,查看模式,然后编写可以递归创建该模式的伪代码,或者,因为这可能是主定理的形式,是否有更简单的数学方法?

现在,假设有人要求您查找由上一次重复创建的程序执行的添加次数的重复次数T(n)。我可以看到基本情况可能是T(1)= T(2)= 0,但我不知道从那里去哪里。

基本上,我问的是如何从给定的重复发生到代码,反之亦然。由于这看起来像主要定理,我想知道是否有一种简单而数学的方法来解决它。

编辑:好的,我已经查看了我过去的一些作业,找到了另一个我被问到的例子,'找到复发',这是这个问题的一部分我遇到了麻烦

  

最佳描述的复发   方式添加操作的次数   在以下程序片段中   (当用l == 1和r == n调用时)

int example(A, int l, int r) {
  if (l == r)
    return 2;
  return (A[l] + example(A, l+1, r);
}

5 个答案:

答案 0 :(得分:8)

几年前,Mohamad Akra和Louay Bazzi证明了一种推广Master方法的结果 - 它几乎总是更好。你真的不应该再使用主定理......

例如,请参阅此文章:http://courses.csail.mit.edu/6.046/spring04/handouts/akrabazzi.pdf

基本上,让你的重复看起来像论文中的等式1,选择系数,并将表达式整合到定理1中。

答案 1 :(得分:2)

扎卡里:

  

让我们说你得到了这个   复发:

     

r(n)= 2 * r(n-2)+ r(n-1); r(1)= r(2)   = 1

     事实上,这是否是以此形式出现的   主定理?如果是这样,话说什么   它在说什么?

我认为你的递归关系所说的是对于“r”的函数,以“n”为参数(表示你输入的数据集的总数),无论你在第n个位置得到什么。数据集是第n-1位的输出加上两次n-2位置的结果,没有进行非递归工作。当您尝试解决递归关系时,您试图以不涉及递归的方式表达它。

但是,我不认为这是主定理方法的正确形式。您的陈述是“具有常系数的二阶线性递归关系”。显然,根据我的旧离散数学教科书,这是你需要的形式,以解决递归关系。

以下是他们提供的表格:

r(n) = a*r(n-1) + b*r(n-2) + f(n)

对于'a'和'b'是一些常数而f(n)是n的一些函数。在你的陈述中,a = 1,b = 2,f(n)= 0.每当f(n)等于零时,递归关系称为“同质”。所以,你的表达是同质的。

我不认为你可以使用Master方法Theoerm来解决同质递归关系,因为f(n)= 0.主方法定理的所有情况都不允许这样做因为n-to-the-power-of - 任何东西都不能等于零。我可能是错的,因为我不是真正的专家,但我不认为使用主法解决同质递归关系是不可能的。

我认为解决同质递归关系的方法是采用5个步骤:

1)形成特征方程式,其形式为:

x^k - c[1]*x^k-1 - c[2]*x^k-2 - ... - c[k-1]*x - c[k] = 0

如果在均匀递归关系中只有2个递归实例,那么你只需要将方程式改为二次方程式

x^2 - a*x - b = 0

这是因为

形式的递归关系
r(n) = a*r(n-1) + b*r(n-2)

可以重写为

r(n) - a*r(n-1) - b*r(n-2) = 0

2)在将递归关系重写为特征方程之后,接下来找到特征方程的根(x [1]和x [2])。

3)根据您的根源,您的解决方案现在将成为以下两种形式之一:

if x[1]!=x[2]
    c[1]*x[1]^n + c[2]*x[2]^n
else
    c[1]*x[1]^n + n*c[2]*x[2]^n

当n> 2时。 4)使用新形式的递归解决方案,使用初始条件(r(1)和r(2))来查找c [1]和c [2]

这里的例子就是我们得到的:

1)     r(n)= 1 * r(n-1)+ 2 * r(n-2) => x ^ 2 - x - 2 = 0

2)解决x

x = (-1 +- sqrt(-1^2 - 4(1)(-2)))/2(1)

    x[1] = ((-1 + 3)/2) = 1
    x[2] = ((-1 - 3)/2) = -2

3)由于x [1]!= x [2],您的解决方案具有以下形式:

c[1](x[1])^n + c[2](x[2])^n

4)现在,使用你的初始条件找到两个常数c [1]和c [2]:

c[1](1)^1 + c[2](-2)^1 = 1
c[1](1)^2 + c[2](-2)^2 = 1

老实说,我不确定在这种情况下你的常数是什么,我在这一点上停了下来。我想你必须插入数字,直到你以某种方式获得c [1]和c [2]的值,这两个值都满足这两个表达式。要么是在矩阵C上进行行减少,要么C等于:

[ 1 1   | 1 ]
[ 1 2   | 1 ] 

扎卡里:

  

最佳描述的复发   方式添加操作的次数   在以下程序片段中   (当用l == 1和r == n调用时)

int example(A, int l, int r) {
  if (l == r)
    return 2;
  return (A[l] + example(A, l+1, r);
}

以下是r> l:

时给定代码的时间复杂度值
int example(A, int l, int r) {      =>  T(r) = 0
  if (l == r)               =>  T(r) = 1
    return 2;               =>  T(r) = 1
  return (A[l] + example(A, l+1, r);    =>  T(r) = 1 + T(r-(l+1))
}

Total:                      T(r) = 3 + T(r-(l+1))

否则,当r == l然后T(r)= 2时,因为if语句和返回都需要每次执行1步。

答案 2 :(得分:1)

使用递归函数用代码编写的方法如下所示:

function r(int n) 
{
  if (n == 2) return 1;
  if (n == 1) return 1;
  return 2 * r(n-2) + r(n-1);  // I guess we're assuming n > 2
}

我不确定“重复”是什么,但递归函数只是一个自我调用的函数。

递归函数需要转义子句(某些非递归情况 - 例如,“如果n == 1返回1”)以防止堆栈溢出错误(即,函数被调用太多,以至于解释器耗尽了内存或其他资源)

答案 3 :(得分:1)

一个简单的程序,它将实现如下:

public int r(int input) {
    if (input == 1 || input == 2) {
        return 1;
    } else {
        return 2 * r(input - 2) + r(input -1)
    }
}

您还需要确保输入不会导致无限递归,例如,如果开头的输入小于1.如果这不是有效的情况,则返回错误,如果它是有效的,然后返回适当的值。

答案 4 :(得分:1)

“我不确定'复发'是什么”

“递归关系”的定义是一个数字序列“其域是一些无限的整数集,其范围是一组实数。”附加条件是描述该序列的函数“根据前一个序列定义序列的一个成员。”

而且,我认为,解决这些问题背后的目标是从递归定义转变为非递归定义。假如你对所有n> 0都有T(0)= 2和T(n)= 2 + T(n-1),你必须从表达式“T(n)= 2 + T(n)开始-1)“对于一个像”2n + 2“。

来源: 1)“离散数学与图论 - 第二版”,作者Edgar G. Goodair和Michael M. Parmenter 2)Ellis Horowitz,Sartaj Sahni和Sanguthevar Rajasekaran的“计算机算法C ++”。