【问题标题】:Time complexity analysis of code代码时间复杂度分析
【发布时间】:2012-09-17 06:11:52
【问题描述】:
int foo(int n) 
{
    int x=2;
    while (x<n)
    {
        x = x*x*x;
    }

    return x;
}

我需要分析它的时间复杂度。我注意到它比log(n) 更快地到达n。我的意思是,它比O(log(n)) 做的步骤更少。我阅读了答案,但不知道他们是如何得到答案的:它是O(log(log(n))。现在,您如何处理这样的问题?

【问题讨论】:

    标签: c time-complexity


    【解决方案1】:

    把它想象成一个递归函数:

    f(i) = f(i-1)^3
    

    如果你展开它:

    f(i) = ((f(i-k)^3)^3)[...k times] = f(i-k)^(3^k) = f(0)^(3^i)
    

    函数随着幂次方增长...所以达到某个数(即计算函数的倒数)的时间(迭代次数)就是对数的对数。

    在您的示例f(0) = 2 中,我们想知道f(i) &gt;= n 何时成为n 输入参数(以及i 迭代次数):

    f(i) = 2^(3^i) >= n
               3^i >= log_2(n)
                 i >= log_3(log_2(n))
    

    所以要达到n 的值,它会进行takes log_3(log_2(n)) 迭代(在处理整数时向上舍入以超过它)。

    如果函数是:

    f(i) = 2*f(i-1) //e.g. x=2*x
    

    那么模式将是:

    f(i) = 2*2*[...k times]*f(i-k) = f(i-k)*(2^k) = f(0)*(2^i)
    

    在这种情况下,函数的倒数将以 2 为底的单个对数。

    我的数学不是很严谨,但我希望你能明白。

    【讨论】:

    • 这确实很有帮助,因为我知道(有点)如何处理递归表达式,例如:T(n) = T(n-1) + C 问题是是否有一种方法可以将任何函数更改为递归的?
    • 是的,任何循环都可以用递归来表示,只要做一个依赖于前面的值的表达式,但有时不是必须的;在这种情况下,跳过它并直接输入解析公式会更容易。
    【解决方案2】:

    想想 x 如何随着循环中的迭代次数而变化。每次,你把它立方体。因此,在 i 次迭代后,该值将是 2 立方,再次立方......等等,i 次。让我们用 x(i) 来表示这个表达式。假设 x(0)=2、x(1)=23 等(我使用 ab 表示 a 的 b 次方)。

    当 x(i)>=n 时,我们就完成了。多久时间?让我们解决 i。

    首先,我们在两边取对数:ln(x(i))>=ln(n) ln(x(i)) = ln(x(i-1))*3 = ln(x(i-2))*(3**2) = ... = ln(x(0))*( 3**i) (以上使用[这个属性][1]:ln(x**b)==ln(x)*b) 所以,3**i * 2 >=ln(n)。让我们取另一个对数: ln(3**i * 2) = ln(2) + ln(3)*i 所以 ln(2) + ln(3)* i >= ln(ln(n)) 现在我们可以求解 i: i >= ( ln(ln(n))-ln(2) ) / ln(3)

    我们可以忽略常数因素,得出的结论是我们将采取 log(log(n)) 步骤。这就是算法的复杂性。

    希望分解所有这样的步骤会有所帮助。

    【讨论】:

    • 这很有帮助,好像我能理解,谢谢
    【解决方案3】:

    L3 = 以 3 为底 L2 = 记录到基数 2

    那么正确答案是 O(L3(L2(n)) 而不是 O(L2(L2(n))。

    x = x * 2 开始。 x 将呈指数增长,直到达到 n,从而使时间复杂度 O(L2(n))

    现在考虑 x = x * x。 x 比上述增长得更快。在每次迭代中,x 的值都会跳到其先前值的平方。做一些简单的数学运算,我们得到以下结果:

    对于 x = 2 n = 4,迭代次数 = 1 n = 16,迭代次数 = 2 n = 256,迭代次数 = 3 n = 65536,迭代次数 = 4

    因此,时间复杂度为 O(L2(L2(n))。您可以通过将值置于 n 值之上来验证这一点。

    现在来解决您的问题,x = x * x * x。这将比 x = x * x 增长得更快。这是表格:

    对于 x = 2 n = 8,迭代次数 = 1 n = 512,迭代次数 = 2 n = (512*512*512),迭代次数 = 3,以此类推

    如果你仔细看的话,结果是 O(L3(L2(n))。L2(n) 会得到 2 的幂,但是因为你取的是立方体x 在每次迭代中,您必须将 log 取到它的底数 3 以找出正确的迭代次数。

    所以我认为正确的答案是O(log-to-base-3(log-to-base-2(n))

    概括这一点,如果 x = x * x * x * x * .. (k 次),则时间复杂度为 O(log-to-base-k(log -to-base-2(n)

    【讨论】:

    • 对数的底与log_a(x) = log_b(x)/log_b(a)O(c·x) = O(x) 对于c = const 无关
    • 是的,我现在意识到了。这有点令人困惑。它给人的印象是 log_4(x) 的运行速度是 log_2(x) 的两倍。
    【解决方案4】:

    如果while循环内的代码是

    x = 2*x;
    

    x 将在 O(log(n)) 次迭代中达到 n。由于您是对 x 进行立方而不是仅仅将其乘以一个常数,因此您将更快地达到 n。

    【讨论】:

    • 不,如果代码为 x = 2*x,则复杂度为 log(n)。用不同的因子对其仍然 O(log(log(n)) 进行平方。
    【解决方案5】:

    给定

    log ( A * x )     == log ( A ) + log ( x )
    log ( x * x * x ) == 3 * log ( x )
    

    所以

    log ( log ( x * x * x ) ) == log ( 3 * log ( x ) )
                              == log ( 3 ) + log ( log ( x ) )
    

    这个函数会比你的函数快多少或慢多少(通过循环的迭代次数来衡量)?

    int log_foo ( int n ) 
    {
        double          log_x = log ( 2 );
        const double    log_n = log ( n );
    
        while ( log_x < log_n )
        {
            log_x = 3 * log_x;
        }
    
        return exp ( log_x );
    }
    

    这个函数会比你的函数快多少或慢多少?

    int log_log_foo ( int n ) 
    {
        double          log_log_x = log ( log ( 2 ) );
        const double    log_log_n = log ( log ( n ) );
        const double    log_3     = log ( 3 );
    
        while ( log_log_x < log_log_n )
        {
            log_log_x += log_3;
        }
    
        return exp ( exp ( log_log_x ) );
    }
    

    但是这个函数只会将log_log_x 增加一个常数,所以很容易算出它做了多少次迭代。

    【讨论】:

    • 第一个会比我的慢,(实际上是我的讲师)关于第二个,我不知道
    • 在考虑 big-O 时,您将操作视为常数时间,因此您需要询问它是否会执行相同数量的迭代。在原始函数中的什么时候它不会执行这两个函数的迭代? x,n > 0 的哪些值将 x
    • 原函数在 x>n 时停止,其中 x 在每次迭代中均取立方。关于你答案的另一部分,我不知道:/ 不是 x log(x) 0 吗?
    • 所以如果条件为真只要对应的条件在原始条件为真,它是否具有相同的迭代次数?
    【解决方案6】:

    i 为迭代步数,x(i)i 步后x 的值。我们有

    x(0) = 2
    x(i) = x(i-1)³
    

    总步数最大的是i所以x(i) &lt; n

    我们有

    log x(i) = log x(i-1)³
             = 3·log x(i-1)
             = 3·log x(i-2)³
             = 3²·log x(i-2)
             = 3^i·log x(0)
             = 3^i·log 2
    
    ⇒  log log x(i) = log (3^i·log 2)
                     = log 3^i + log log 2
                     = i·log 3 + log log 2
    

    对数严格递增,所以

    x(i) < n ⇔        log log x(i) < log log n
             ⇔ i·log 3 + log log 2 < log log n
             ⇔                   i < (log log n - log log 2) / log 3 ∈ O(log log n)
    

    【讨论】:

      【解决方案7】:

      为什么不添加一个计数器变量来计算循环的迭代次数。在函数返回之前打印出来。

      然后为一系列值调用函数,例如从 3 到 1,000,000 开始。然后使用GNUPlot 之类的东西绘制你的结果。

      然后查看图形是否与已知曲线匹配。

      【讨论】:

      • 这是个好主意,但这是一个考试题,我应该知道如何在没有这个工具的情况下解决这个问题
      猜你喜欢
      • 2015-02-21
      • 1970-01-01
      • 2016-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多