【问题标题】:Why time complexity for permutation algorithm is n*n! and not n^3 * n!?为什么置换算法的时间复杂度是 n*n!而不是 n^3 * n!?
【发布时间】:2021-12-22 03:50:54
【问题描述】:

作者声称这种置换算法的时间复杂度:

from collections import deque

def find_permutations(nums):
    
    nums_l = len(nums)
    perms = deque()
    perms.append([])
    
    for num in nums:                     # (1)
        for _ in range(len(perms)):      # (2)
            perm = perms.popleft() 
            for j in range(len(perm)+1): # (3)
                new_perm = list(perm)    # (4)
                new_perm.insert(j, num)  # (5)
                perms.append(new_perm)   # (6)
    
    return perms

n*n!

但我不明白为什么。这是我对每一行代码的时间复杂度的计算:

(1): len(nums) ~ n
(2): len(perms) ~ n!
(3): len(perm) ~ n
(4): n
(5): n
(6): n

所以总时间复杂度是:

n * n! * n * (n + n + n) ~ n^3 * n!

我错了吗?

【问题讨论】:

    标签: algorithm time-complexity permutation


    【解决方案1】:

    在讨论主要问题之前,第 (6) 步有一个小错误:append 是 O(1)。但这并不影响整体的复杂性。

    您的分析在第 (2) 步出错了:

    for _ in range(len(perms)):
    

    这不会执行 O(n!) 次迭代。第一次它甚至只执行1次迭代,第二次还是1次迭代,第三次2次迭代,然后是2 * 3,然后是2 * 3 * 4,……最后一次(n-1)!迭代。

    那么在第(3)步也有高估:

    for j in range(len(perm)+1)
    

    perm 的大小最初很小(对应于步骤 2 的循环):首先排列的大小为 0,然后在外循环的下一次迭代中,它们的大小为 1,...然后2, ... 最多 n-1。

    所以对于内循环的次迭代,我们有这个总和:

    1 * 1 + 1 * 2 + 2 * 3 + (2 * 3) * 4 + ... + k! + ... 不!

    了解这一点的一种更简单的方法是认识到,在外循环的每次迭代中,此代码都会产生比其前一次迭代长一个项目的所有排列。因此,在第一次迭代中,您获得所有大小为 1 的排列(使用第一个输入值),在第二次迭代中,您获得所有大小为 2 的排列(使用两个第一个输入值),......等等。这是一种自下而上的方法。另请注意,popLeft 被执行了正确的次数以删除前一个(外部)迭代的所有排列,而 append 被执行用于所有新排列(仅在下一个外部迭代中弹出)。

    所以我们现在可以很容易地看到append 被执行了这么多次:

    1! + 2! + 3! + 4! + ... + n! = O(n!)

    如果我们现在将步骤 4 的复杂性包括在内(它“吸收”了步骤 5 的复杂性),那么我们得到:

    1.1! + 2.2! + 3.3! + 4.4! + ... + n.n! = O(n.n!)

    【讨论】:

    • 第 1 步的复杂性如何?它也应该是 O(n)。所以看起来总复杂度将是 O(n.n.n!),对吧?谢谢。
    • 不,当我写 "for the total number of iterations of the inner loop" 时,第 1 步已包含在内——我更新了一个短语表示考虑了外部循环。
    • 看来我终于明白了。如果我们将置换算法与时间复杂度为 n^2 的东西进行比较,例如[i for i in range(n) for j in range(n)] 不同之处在于,对于置换算法,我们在每一步都有不同数量的异常,因此时间复杂度将只是最后一步的复杂度(n!)。但是对于 n^2 算法,我们为每个外循环执行 n 次,例如对于 n =5:O(5+5+5+5+5) = O(5*5) 或 O(n^2)。为了将其与置换算法进行比较,我们的时间复杂度为 O(1! + 2! + 3! + 4! +...),最终的复杂度将只是最后一步的复杂度 (n!)
    【解决方案2】:

    该算法通过将数字插入现有排列的所有可能位置来生成排列

    现在第一个循环显然是O(n)

    让我们看看其他循环发生了什么:

    在最终迭代中(最坏的情况):

    1. 遍历所有(n-1)! 排列(由以前的迭代生成)-O((n-1)!)
    2. 复制它们中的每一个并在其中添加新数字 - O(n) each

    对于其他循环的最坏情况,总共是 (n-1)! * (n) = n!

    所以它是O(n * n!) 整体

    【讨论】:

    • 不是 O(n * n!) 只是 O(n!) 在 Big O 中吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-13
    • 2021-11-11
    • 2022-10-24
    • 2020-03-22
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多