如何确定双重递归函数的运行时间?


15

给定任意双递归函数,将如何计算其运行时间?

例如(用伪代码):

int a(int x){
  if (x < = 0)
    return 1010;
  else
    return b(x-1) + a(x-1);
}
int b(int y){
  if (y <= -5)
    return -2;
  else
    return b(a(y-1));
}

或类似的规定。

一个人可以或应该使用什么方法来确定类似的东西?


2
这是作业吗?
伯纳德

5
不,现在是夏天,我喜欢学习。我想取得成功,而不是让我的大脑陷入沉思。
if_zero_equals_one 2011年

11
好,知道了。对于那些投票赞成将其迁移到Stack Overflow的人:这是此处的主题,而Stack Overflow则是离题。程序员。SE适用于概念性白板问题。堆栈溢出用于实现,我正在编码问题。

3
谢谢,这就是我首先在这里这样做的原因。另外,比起接受一条鱼,更好地知道如何钓鱼。
if_zero_equals_one 2011年

1
在这种特殊情况下,由于b(a(0))调用了许多其他b(a(0))项,因此通常仍然是无限递归。如果它是一个数学公式,那将是不同的。如果您的设置不同,那么效果会有所不同。就像在数学中一样,在CS中有些问题有解决方案,有些没有解决方案,有些有简单的解决方案,有些则没有。解决方案的确存在许多相互递归的情况。有时,为了不炸栈,必须使用蹦床模式。
工作

Answers:


11

您一直在更改功能。但是请继续选择那些无需转换即可永久运行的产品。

递归变得复杂,快速。分析提议的双递归函数的第一步是尝试通过一些样本值对其进行跟踪,以查看其功能。如果您的计算陷入无限循环,则该函数定义不正确。如果您的计算陷入不断增加的螺旋式上升(这很容易发生),则可能定义不明确。

如果通过跟踪找到答案,则可以尝试得出答案之间的某种模式或递归关系。一旦有了它,就可以尝试找出其运行时间。弄清楚它可能非常非常复杂,但是我们得到了诸如Master定理之类的结果,这些结果使我们在很多情况下都能找到答案。

请注意,即使是单次递归,也很容易提出我们不知道如何计算其运行时间的函数。例如,考虑以下内容:

def recursive (n):
    if 0 == n%2:
        return 1 + recursive(n/2)
    elif 1 == n:
        return 0
    else:
        return recursive(3*n + 1)

它是目前未知是否这个功能总是明确定义,更不用说它的运行时间是什么。


5

该对特定函数的运行时间是无限的,因为两个函数都不返回而没有调用另一个函数。的返回值a始终依赖于调用的返回值b,其始终要求a......那什么作为的无限递归


没有在这里寻找特定的功能。我正在寻找一种通用的方法来查找相互调用的递归函数的运行时间。
if_zero_equals_one 2011年

1
我不确定一般情况下是否有解决方案。为了使Big-O有意义,您必须知道算法是否会停止。在知道要花费多长时间之前(例如确定某个点是否属于Mandlebrot集),必须先执行一些递归算法。
jimreed 2011年

并非总是如此,ab在传入的数字> = 0 时才调用。但是,是的,存在无限循环。
btilly 2011年

1
@btilly我发布答案后,示例已更改。
jimreed 2011年

1
@jimreed:它又被更改了。如果可以的话,我会删除我的评论。
btilly 2011年

4

一种明显的方法是运行该功能并测量所需的时间。但是,这仅告诉您进行特定输入需要多长时间。而且,如果您事先不知道该函数会终止,那就太难了:没有机械的方法可以确定该函数是否终止-这是一个停顿的问题,而且还无法确定。

根据赖斯定理,类似地,无法确定函数的运行时间。实际上,赖斯定理表明,即使确定一个函数是否O(f(n))及时运行也是不确定的。

因此,一般而言,您能做的最好的事情就是利用您的人类智慧(据我们所知,不受图灵机的限制),并尝试识别一种模式或发明一种模式。分析函数运行时间的一种典型方法是将函数的递归定义转换为关于其运行时间的递归方程式(或一组相互递归函数的方程式):

T_a(x) = if x ≤ 0 then 1 else T_b(x-1) + T_a(x-1)
T_b(x) = if x ≤ -5 then 1 else T_b(T_a(x-1))

接下来是什么?您现在遇到了一个数学问题:您需要解决这些函数方程。一种常用的方法是将整数函数上的这些方程式转换为解析函数上的方程式,并使用演算来求解这些函数,解释函数T_a并将其T_b作为生成函数

关于生成函数和其他离散数学主题,我推荐Ronald Graham,Donald Knuth和Oren Patashnik 撰写的《具体数学》一书。


1

正如其他人指出的那样,分析递归会非常困难。这是这种情况的另一个示例:http : //rosettacode.org/wiki/Mutual_recursion http://en.wikipedia.org/wiki/Hofstadter_sequence#Hofstadter_Female_and_Male_sequences 很难计算出这些答案和运行时间。这是由于这些相互递归的函数具有“困难形式”。

无论如何,让我们来看一个简单的例子:

http://pramode.net/clojure/2010/05/08/clojure-trampoline/

(declare funa funb)
(defn funa [n]
  (if (= n 0)
    0
    (funb (dec n))))
(defn funb [n]
  (if (= n 0)
    0
    (funa (dec n))))

让我们开始尝试计算funa(m), m > 0

funa(m) = funb(m - 1) = funa(m - 2) = ... funa(0) or funb(0) = 0 either way.

运行时间是:

R(funa(m)) = 1 + R(funb(m - 1)) = 2 + R(funa(m - 2)) = ... m + R(funa(0)) or m + R(funb(0)) = m + 1 steps either way

现在让我们选择另一个稍微复杂一点的示例:

受到http://planetmath.org/encyclopedia/MutualRecursion.html的启发,它本身很不错,让我们看一下:“”“斐波那契数可以通过相互递归来解释:F(0)= 1和G(0 )= 1,其中F(n + 1)= F(n)+ G(n),而G(n + 1)= F(n)。“”“

那么,F的运行时间是多少?我们将走另一条路。
好吧,R(F(0))= 1 = F(0); R(G(0))= 1 = G(0)
现在R(F(1))= R(F(0))+ R(G(0))= F(0)+ G(0)= F (1)
...
不难看出R(F(m))= F(m)-例如,计算索引i处的斐波那契数所需的函数调用次数等于斐波那契数的值在索引i。假设将两个数字相加比函数调用要快得多。如果不是这种情况,那就是对的:R(F(1))= R(F(0))+ 1 + R(G(0)),对此的分析将更加复杂,可能没有简单的封闭式解决方案。

斐波那契数列的封闭形式不一定很容易被重塑,更不用说一些更复杂的例子了。


0

首先要做的是显示您定义的函数终止以及确切的值。在示例中,您已定义

int a(int x){
  if (x < = 0)
    return 1010;
  else
    return b(x-1) + a(x-1);
}
int b(int y){
  if (y <= -5)
    return -2;
  else
    return b(a(y-1));
}

b仅终止于此,y <= -5因为如果您插入任何其他值,那么您将具有格式的条款b(a(y-1))。如果再做一些扩展,您会看到形式b(a(y-1))的术语最终会导致术语b(1010),导致术语b(a(1009))再次导致术语b(1010)。这意味着您不能将任何a不满足的值插入其中,x <= -4因为如果这样做,您将陷入无限循环,其中要计算的值取决于要计算的值。因此,基本上这个示例具有恒定的运行时间。

因此,简单的答案是,没有通用的方法来确定递归函数的运行时间,因为没有通用的过程来确定递归定义的函数是否终止。


-5

运行时是否像Big-O一样?

这很容易:O(N) -假设存在终止条件。

递归只是循环,而无论您在该循环中执行了多少操作,一个简单的循环都是O(N)(调用另一个方法只是循环中的另一步)。

如果您在一个或多个递归方法中有一个循环,它将变得很有趣。在那种情况下,您将获得某种指数的性能(方法每次通过时乘以O(N))。


2
您可以通过采用任何调用方法的最高顺序并将其乘以调用方法的顺序来确定Big-O性能。但是,一旦开始讨论指数和阶乘性能,就可以忽略多项式性能。我认为在比较指数和阶乘时也是如此:阶乘获胜。我从来没有来分析,这是一个系统,两个指数和阶乘。
佚名

5
这是不正确的。计算第n个斐波那契数和快速排序的递归形式分别为O(2^n)O(n*log(n))
Unpythonic 2011年

1
在不做任何花哨的证明的情况下,我想将您定向到amazon.com/Introduction-Algorithms-Second-Thomas-Cormen/dp/…,然后尝试查看此SE网站 cstheory.stackexchange.com
布莱恩·哈灵顿

4
人们为什么会投票给这个可怕的错误答案?调用一个方法所花费的时间与该方法所花费的时间成正比。在这种情况下,方法a调用bb调用a,所以你不能简单地认为这两种方法需要一定的时间O(1)
btilly 2011年

2
@Anon-发布者要求的是任意双递归函数,而不仅仅是上面显示的那个。我给出了两个简单的递归示例,它们不符合您的解释。将旧标准转换为“双递归”形式很简单,一种形式是指数形式(适合您的警告),另一种形式不是指数形式(未涵盖)。
Unpythonic 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.