【问题标题】:What is the time complexity of the following nested loops in Big-Theta notation?Big-Theta 表示法中以下嵌套循环的时间复杂度是多少?
【发布时间】:2016-11-18 16:06:34
【问题描述】:
for (int i = 1; i < n; i++)
    for (int j = i; j < n; j *= 2)
        for (int k = j; k < n; k *= 2);

我知道时间复杂度是 O(n.log2(n)),但我希望它采用 Big-Theta 表示法,并且想知道如何证明这确实是时间复杂度。

【问题讨论】:

    标签: algorithm time-complexity nested-loops big-theta


    【解决方案1】:

    Big Theta 是在最坏和最好的情况下时间复杂度相同的符号。

    由于这是您手头的情况,您可以在 Big Theta 的大 O 中写下您所拥有的内容。

    当涉及其他未知数据(例如大小为 n 的数组,但其中包含您事先不知道的值时,您会在最坏/最佳情况下有所不同。在这种情况下,算法可能具有不同的运行时间,具体取决于该数据。另一个例子是当您涉及一些随机生成器时(这只是获取未知数据的另一种方式)。

    但由于您的算法中没有任何未知数据,因此可以使用 Big Theta 表示法来编写时间复杂度。

    时间复杂度证明:

    然而,复杂性不是 Θ(n.log2(n))。它实际上是一个普通的 Θ(n)

    时间复杂度可以从这个扩展代码中count变量的增量数推导出来,因为对于外循环的每次迭代,中间的循环至少执行一次,并且对于外循环的每次迭代中间一个,最里面的循环也至少执行一次:

    for (i = 1; i < n; i++) {
        for (j = i; j < n; j *= 2)
            for (k = j; k < n; k *= 2)
                count++;
    

    内部的两个循环与此替代(伪)代码中的增量相同,其中 log 代表以 2 为底的对数:

        for (j = log(i); j < log(n); j++)
            for (k = j; k < log(n); k++)
                count++;
    

    也可以:

        for (j = 0; j < log(n) - log(i); j++)
            for (k = j; k < log(n) - log(i); k++)
                count++;
    

    m 定义为 j 上的循环对给定 i 进行的迭代次数,然后是 m > 是 log(n) - log(i) 向上舍入到最接近的整数。给定 i 的增量数对应于从 m 值中选择 2 个值的方法数,其中第二个值大于或等于第一个值。这个数字是m⋅(m+1)/2

    除去向上舍入,我们可以通过将 log(n) - log(i) 加一来找到 m 的上限。让我们将 m 重新定义为这个上限:

    m = log(n) − log(i) + 1

    然后我们得到这个由给定 i 的内部两个循环增加的 count 数量的上限:

    ½m⋅(m+1)

    将其分解为对数部分,我们得到:

    ½(log(n) − log(i) + 1)⋅(log(n) − log(i) + 2)
       = ½log2 (n) + (3/2)⋅log(n) + 1 + (−log(n)−3/2)⋅log(i) + ½log2(i)

    加上外循环的效果,我们得到了整体count的上限:

    计数 2(n) + (3/2)⋅n⋅log(n) + n + (−log(n)−3/2)⋅ Σilog(i) + ½Σilog2(i)

    需要解决 i 的总和。对数之和是乘积的对数,所以我们有这个等式:

    Σilog(i) = log(n!)

    n来自外循环的迭代次数。其实外层循环并没有迭代那么多次,因为它的最后一次迭代是用i = n − 1,但是为了定义一个上界,我们可以选择取 n 也在船上。

    applying the Stirling formula 将一些自然对数(此处记为 ln)转换为基于 2 的对数,我们得到了这个有用的渐近近似:

    log(n!) = (n+½)⋅log(n) - n/ln(2) + ½log(2π) + O(1/n)
       = n⋅log(n) + (−1/ln(2))⋅n + ½⋅log(n) + ½log(2π) + O(1/n)

    对于带有总和的最后一项,Σilog2(i),我们可以使用Euler-Maclaurin formula,并使用integral over this function(另见"sum of log squared terms"),我们得出:

    Σilog2(i) = n⋅[log2(n) - (2/ln(2 ))⋅log(n) + 2/ln2(2)] − 2/ln2(2) + ½log2(n) + O(log(n)/n)
       = n⋅log2(n) - ( 2/ln(2))⋅n⋅log(n) + (2/ln2(2))⋅n + (−2)/ln2(2 ) + ½log2(n) + O(log(n)/n)

    将其纳入我们为 count 提供的内容中:

    计数 2(n) + (3/2)⋅n⋅log(n) + n
       + (− log(n)−3/2)⋅Σilog(i)
       + ½Σilog2 sup>(i)

    我们得到:

    计数 2(n) + (3/2)⋅n⋅log(n) + n
       + (− log(n)−3/2)⋅[ n⋅log(n) + (−1/ln(2))⋅n + ½⋅log(n) + ½log(2π) ]
       + ½[ n⋅log2(n) - (2/ln(2))⋅n⋅log(n) + (2/ln2(2 ))⋅n + (−2)/ln2(2) + ½log2(n)]
       + O(log (n)/n)

    = ½n⋅log2(n) + (3/2)⋅n⋅log(n) + n
       + (−n −½)⋅log(n) + (1/ln(2))⋅n⋅log(n) + (−½)⋅log(2π)⋅log(n) + (−3n/2−3/4) ⋅log(n) + (3/(2ln(2)))⋅n + (−3/4)⋅log(2π)
       + ½n⋅log2 sup>(n) − (1/ln(2))⋅n⋅log(n) + (1/ln2(2))⋅n + (−1)/ln 2(2) + (1/4)⋅log2(n)
       + O(log(n)/n)

    将所有在n方面具有相同意义的术语放在一起,结果是因子为n⋅log2(n) 和 n⋅log(n) 相互消除。所以我们得到:

    计数 2(2))⋅n
       + (−1/4)⋅log2(n) + ((−1/2)⋅log(2π)−3/4)⋅log(n)
       + (−3/4)⋅log(2π) + (−1)/(ln2(2))    + O(log(n)/n)

    所以这是 ... Θ(n),并且为 (1 + 3/(2⋅ln(2)) + 1/ln2(2)) 大约是 5.24 并且少数几个 next 表达式具有负系数,count 存在以下上限:

    计数

    这是一个上限。它不能比 Θ(n) 更好,因为外部循环执行 Θ(n) 次。

    测试它

    根据经验,您可以看到表达式 count/n 对于大 n 收敛到值 4。这是一个保持增量 n 的小 sn-p,执行算法并输出这个分数:

    function f(n) {
        var i, j, k, count = 0;
        for (i = 1; i <= n; i++) {
            for (j = i; j < n; j *= 2)
                for (k = j; k < n; k *= 2)
                    count++;
        }
        return count;
    }
    
    // keep increasing n forever, and print f(n)/n
    var n = 2;
    setInterval(function () {
        document.body.textContent = 'n = ' + n 
                                  + '\nf(n) = ' + f(n)
                                  + '\nf(n)/n = ' + (f(n)/n);
        n = n + 1;
    }, 1);
    body { white-space: pre }

    注意:我觉得必须有一个更简洁和优雅的证明,但这是我现在能想到的。

    【讨论】:

    • 不应该是Theta(n * log(n) * log(log(n))) 吗?
    • @trincot 我知道我的代码在最好和最坏的情况下运行相同。但为什么它是 theta(n.log(n))。我需要一个证明。
    • @trincot 为什么不应该是Theta(n * log(n) * log(log(n))) 甚至Theta(n * log(n))
    • 你的陈述是“我知道时间复杂度是O(n.log2(n))”,所以你现在是说你不知道吗?因为如果知道那是真的,那么 Big Theta 就是这样。您的问题是关于符号的:“...但我希望它采用 Big-Theta 符号。”。现在我觉得你的问题不同了。
    猜你喜欢
    • 1970-01-01
    • 2015-12-21
    • 2017-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多