【问题标题】:Time complexity Recursive program时间复杂度递归程序
【发布时间】:2017-08-17 11:22:05
【问题描述】:

我无法弄清楚以下 sn-p 代码的时间复杂度:

void  f( int N ){
   sum ++;
   if ( N   > 1){
      f( N /2);
      f( N /2);
   }

这是给我带来问题的双重迭代。

我知道(或认为)的时间复杂度

void  f( int N ){
   sum ++;
   if ( N   > 1){
      f( N /2);
   }

是 ~log2(N) 但不知道如何处理其他代码。

【问题讨论】:

  • 我没有答案,但是看看你是否可以为一些小的输入计算出这个函数。也许复杂性对您来说是显而易见的。

标签: time-complexity


【解决方案1】:

您在(N/2) 上调用了两次递归。让我们写公式:

T(n) = 2*(N/2) + 1

使用master theorem,我们会遇到第一种情况:

  • a=2
  • f = O(1)
  • b=2

T(n) = Θ(n)

我们还发现 T(n) = 2*T(n/2) + 1 here,这表明它以 O(n) 为界

【讨论】:

    【解决方案2】:

    解决这个问题的一个好方法是:


    1.寻找递归关系

    对于每次调用f,我们都有时间复杂度T(N)。每个调用包含:

    • 恒定的工作量C:sum++、比较N > 1、递归调用开销等。
    • 2 次递归调用 f,每个都具有时间复杂度 T(N / 2)

    因此递归关系T(N) = 2T(N/2) + C给出。


    2.通过检查找到解决方案

    如果我们反复将T(N) 替换为自身,我们可以看到一个新兴模式:

    求和的上限是多少,m?由于停止条件为N > 1,重复替换后要求为

    因此总和等于(舍弃四舍五入和常数C,因为它只是乘法):


    3.归纳证明

    • 基本步骤:测试求和结果对于N的最低可能值是否正确,即2:

      结果是一致的。

    • 递归步骤:确认如果对N / 2求和正确,对N也正确:

      这正是我们原来的递归关系。

    因此通过归纳,求​​和结果是正确的,T(N)确实是Ө(N)


    4.数值测试:

    如果需要,我们可以编写代码来确认我们的结果:

    function T(n) {
      return n > 1 ? 2 * T(floor(n/2)) + 1 : 1;
    }
    

    结果:

    N           T(N)
    -------------------------
    2           3
    4           7
    8           15
    16          31
    32          63
    64          127
    128         255
    256         511
    512         1023
    1024        2047
    2048        4095
    4096        8191
    8192        16383
    16384       32767
    32768       65535
    65536       131071
    131072      262143
    262144      524287
    524288      1048575
    1048576     2097151
    2097152     4194303
    4194304     8388607
    8388608     16777215
    16777216    33554431
    33554432    67108863
    67108864    134217727
    134217728   268435455
    268435456   536870911
    536870912   1073741823
    1073741824  2147483647
    

    图表:

    【讨论】:

    • 您的回答没有得到应有的重视。很好很详细的回答! +1。
    • @TonyTannous 谢谢。你也是 - 在这样的简单情况下使用主定理可能比从头开始证明更好的方法
    【解决方案3】:

    让我们尝试追踪它:

    令 N 为 8

    1 + f(4) + f(4)
    => 1 + 2 + f(2) + f(2) + f(2) + f(2)
    => 1 + 6 + f(1) + f(1) + f(1) + f(1) + f(1) + f(1) + f(1) + f(1)

    当 n = 8 时;工作 = 15

    令 N 为 4

    1 + f(2) + f(2)
    => 1 + 2 + f(1) + f(1) + f(1) + f(1)

    当 n = 4;工作 = 7

    令 N 为 2

    1 + f(1) + f(1)

    当 n = 2;工作 = 3;

    令 N 为 1

    1

    当 n = 1 时;工作 = 1


    所以乍一看,工作模式似乎是 2n - 1

    我们还需要证明!

    从算法中递归关系为:

    W(1) = 1
    W(N) = 1 + 2 * W(N / 2)

    归纳证明

    基本情况:
    W(1) = 2(1) - 1 = 1 根据需要

    递归案例:
    假设 W(N / 2) = 2(n/2) - 1 = n - 1

    W(N) = 1 + 2 * (N / 2)
    应用归纳...
    W(N) = 1 + 2(n - 1) = 1 + 2n - 2 = 2n - 1 根据需要


    因此复杂度为 O(2n - 1),因为 O 是自反的

    => O( max { 2n, -1 } ) 因为求和规则
    => O(2n)

    => O(n) 因为缩放规则

    【讨论】:

    • 谢谢。只是为了确定,我对第二个问题的解决方案是否正确?
    • n = 16,w = 5; n = 8, w = 4; n = 4,w = 3; n = 2,w = 2; ---> 在我看来它是对数的
    【解决方案4】:

    这段代码很像二叉树遍历

    void  f( int N ){
       sum ++;
       if ( N   > 1){
          f( N /2);  //like Traverse left subtree
          f( N /2);  //like Traverse right subtree
       }
    

    基本上遍历每个节点一次,时间复杂度为O(N)

                        n/8
                 n/4   --------- 
                        n/8
          n/2   ------------------    
    
                        n/8
                 n/4  ---------
                        n/8
    
    n -------------------------------
                        n/8
                 n/4    --------- 
                        n/8
          n/2   ----------------
    
                        n/8
                 n/4   ---------
                        n/8
    

    一直持续到传递的值变为 1 或 0。

    【讨论】:

      猜你喜欢
      • 2023-03-11
      • 1970-01-01
      • 2013-03-13
      • 2017-07-07
      • 1970-01-01
      • 2020-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多