这个用于计算指数的递归代码的运行时是什么?

时间:2017-03-06 23:43:52

标签: c++ algorithm recursion time-complexity big-o

以下函数的运行时间复杂度是O(1)吗?

int pow(int a, int n) {
    if (n == 0) {
        return 1;
    }
    if (n % 2 == 1) {
        return pow(a, n / 2) * pow(a, n / 2) * a;
    } else {
        return pow(a, n / 2) * pow(a, n / 2);
    }
}

我受到了这种印象,因为代码中只有if语句,没有循环。我之前从未使用过Big-O和递归,我在网上找不到任何好的资源。

3 个答案:

答案 0 :(得分:4)

您的函数的运行时为O(n),但可以很容易地修改它以在时间O(log n)中运行。

我们可以通过很多方式看到这一点。首先,我们可以计算正在进行的递归调用的总数,因为每个递归调用都会执行O(1)工作。想象一下,例如,我们将pow(a,8)称为某个数字a。然后

  • pow(a,8)两次调用pow(a,4)。
  • pow(a,4)两次调用pow(a,2)。
  • pow(a,2)两次调用pow(a,1)。
  • pow(a,1)两次调用pow(a,0)。

这意味着有

  • 1打电话给pow(a,8),
  • 2次致电pow(a,4),
  • 4次致电pow(a,2),
  • 8次调用pow(a,1)和
  • 16次致电pow(a,0)。

总的来说,这是1 + 2 + 4 + 8 + 16 = 31总的来电。

现在,假设我们称之为pow(a,16)。这将触发对pow(a,8)的两次调用(总共62次递归调用),以及一次初始调用pow(a,16)以进行总共63次递归调用。如果我们调用pow(a,32),我们将两次调用pow(a,16)(总共126次递归调用),另外一次调用pow(a,32),总共127次递归调用。更一般地说,似乎我们称之为pow(a,n),我们会得到4n - 1次调用,这将是O(n)。

我们实际上可以正式证明这一点。设C(n)是对大小为n的输入进行的调用次数。注意

  

C(0)= 1。   C(n)= 2C(n / 2)+ 1

这种复发通过主定理解决了O(n)。

请注意,每个单独的递归调用都在做很少的工作。杀死我们的是这样一个事实:有很多完整的递归调用,这些调用会使这些调用累加起来。但是,虽然有很多递归调用,但很少有唯一的递归调用。因此,请考虑代码的这种变化:

int pow(int a, int n) {
    if (n == 0) return 1;

    int halfPow = pow(a, n / 2);
    if (n % 2 == 0) return halfPow * halfPow;
    else return halfPow * halfPow * a;
}

此代码缓存正在进行的递归调用的值,因此它每次都会触发一次调用。结果,每次调用完成的工作仍然是O(1),但在递归中没有更多的分支。然后,因为每个递归调用的大小是原始的的一半,并且因为每个级别只有一个调用,所以运行时可以运行到O(log n),您可以使用Master确认定理。

一般来说,要警惕“我们不断削减一半的形式”的论据,因此整体工作最终成为O(log n)。这可能是真的,但是你在每一步所做的工作量对于确定运行时也非常非常重要,正如你在这里看到的那样。

答案 1 :(得分:2)

让我们分解这里发生的事情。实际上是O(n)。对于每次调用pow(),都有两种选择:

  1. 返回0 - 这是一次操作。
  2. 用n / 2执行两次对pow()的调用。
  3. 因此,在每次调用pow()时,将n减少n / 2,可以在每一步上以指数方式增加问题空间。递归的第一步是两次调用pow,第二步是2 ^ 2,第三步是2 ^ 3等。对于n = 16,将有pow(...,16),pow(...,8),pow( ...,4),pow(...,2),pow(...,1),pow(...,0),其中2 ^ 0调用pow为n = 16,2 ^ 1为n对于n = 4,为2 ^ 2,对于n = 2,为2 ^ 3,对于n = 1,2 ^ 4,对于n = 0,为2 ^ 5。

    因此,我们称之为pow log(n)次,但pow调用的每次迭代都会使上一步中的调用次数增加一倍。这意味着我们有O(n)

答案 2 :(得分:1)

不,因为它涉及递归和分支。其时间复杂度为O(n),其空间复杂度为O(log n)

您将获得O(log n)时间复杂度:

int pow (int a, int n) {
    if(n == 0) { return 1; }
    int halfpow = pow(a,n/2);
    return halfpow * halfpow * (n % 2 == 1 ? a : 1);
}