如何为给定的代码片段编写递归关系

时间:2015-05-12 21:05:37

标签: algorithm recurrence

在我的算法和数据结构类中,我们给了一些递归关系来解决或者我们可以看到算法的复杂性。

起初,我认为这些关系的唯一目的是记下递归分而治之算法的复杂性。然后我在麻省理工学院的作业中遇到了一个问题,其中一个被要求为迭代算法提供递归关系。

如果给出一些代码,我怎么能自己想出一个递归关系呢?有什么必要的步骤?

我是否可以记下任何情况,即最坏的,最好的,具有这种关系的平均情况,这是否正确?

有可能有人举例说明一段代码如何变成递归关系吗?

干杯, 安德鲁

2 个答案:

答案 0 :(得分:13)

好的,在算法分析中,递归关系是一个函数,它将解决大小为n的问题所需的工作量与解决较小问题所需的工作量相关联(这与其在数学中的含义密切相关)。

例如,考虑下面的Fibonacci函数:

Fib(a) 
{
  if(a==1 || a==0)
    return 1;
  return Fib(a-1) + Fib(a-2);
}

这会执行三个操作(比较,比较,添加),并且还会递归调用自身。因此递归关系为T(n) = 3 + T(n-1) + T(n-2)。要解决此问题,您可以使用迭代方法:开始扩展术语,直到找到模式。对于此示例,您可以展开T(n-1)以获取T(n) = 6 + 2*T(n-2) + T(n-3)。然后展开T(n-2)以获取T(n) = 12 + 3*T(n-3) + 2*T(n-4)。再一次,展开T(n-3)以获得T(n) = 21 + 5*T(n-4) + 3*T(n-5)。请注意,第一个T项的系数跟随Fibonacci数,而常数项是它们之和乘以三:查找它,即3*(Fib(n+2)-1)。更重要的是,我们注意到序列呈指数增长;也就是说,算法的复杂性是O(2 n )。

然后考虑使用此函数进行合并排序:

Merge(ary)
{
  ary_start = Merge(ary[0:n/2]);
  ary_end = Merge(ary[n/2:n]);

  return MergeArrays(ary_start, ary_end);
}

此函数在输入的一半上调用自身两次,然后合并两半(使用O(n)工作)。也就是T(n) = T(n/2) + T(n/2) + O(n)。要解决此类型的递归关系,您应该使用Master Theorem。通过这个定理,这扩展到T(n) = O(n log n)

最后,考虑这个函数来计算Fibonacci:

Fib2(n)
{
  two = one = 1;
  for(i from 2 to n)
  {
    temp = two + one;
    one = two;
    two = temp;
  }
  return two;
}

此函数不会自行调用,并且迭代O(n)次。因此,其递归关系为T(n) = O(n)。你问的是这种情况。这是一种复发关系的特例,没有再发生;因此,它很容易解决。

答案 1 :(得分:1)

要查找算法的运行时间,我们首先需要为算法编写表达式,该表达式告诉每个步骤的运行时间。因此,您需要遍历算法的每个步骤以查找表达式。 例如,假设我们定义了一个谓词isSorted,它将数组a和数组的大小n作为输入,并且当且仅当数组按递增顺序排序时才返回true。

bool isSorted(int *a, int n) {
   if (n == 1)
      return true;       // a 1-element array is always sorted
   for (int i = 0; i < n-1; i++) {
      if (a[i] > a[i+1]) // found two adjacent elements out of order
         return false;
   }
   return true;         // everything's in sorted order
}

显然,此处输入的大小将简单为n,即数组的大小。对于输入n?

,在最坏的情况下将执行多少步骤

第一个if语句计为1步

在最坏的情况下,for循环将执行n-1次(假设内部测试没有将我们踢掉),循环测试的总时间为n-1,并且增量为索引。

在循环中,还有另一个if语句,每次迭代将执行一次,总共n-1次,最坏的情况。

最后一次返回将执行一次。

所以,在最坏的情况下,我们已经完成了1+(n-1)+(n-1)+1

计算,总运行时间T(n)≤1+(n-1)+(n-1)+ 1 = 2n,因此我们得到定时函数T(n)= O(n)。< / p>

简而言之,我们所做的是 - &gt;&gt;

1.参数&#39; n&#39;给出输入的大小我们假设每个执行一次的简单语句将花费恒定的时间,为简单起见假设一个

2.循环和内部主体之类的迭代语句将根据输入采用可变时间。 其中有解决方案T(n)= O(n),就像非递归版本一样。

3.所以你的任务就是一步一步地用n来写下函数来计算时间复杂度

对于递归算法,你做同样的事情,只是这次你添加每次递归调用所花费的时间,表示为它输入所用时间的函数。 例如,让我们重写,isSorted作为递归算法:

bool isSorted(int *a, int n) {
   if (n == 1)
      return true;
   if (a[n-2] > a[n-1])         // are the last two elements out of order?
      return false;
   else
      return isSorted(a, n-1); // is the initial part of the array sorted?
}

在这种情况下,我们仍然会遍历算法,计算:第一个if步骤加1步为第二个if,加上时间isSorted将采用大小为n-1的输入,这将是T(n -1),给出递归关系

T(N)≤1+ 1 + T(N-1)= T(N-1)+ O(1)

其中有解决方案T(n)= O(n),就像非递归版本一样。

简单够了!!练习更多来编写各种算法的递归关系,记住每个步骤在算法中执行的时间