【问题标题】:What is the Time Complexity of finding the max k integers?找到最大 k 个整数的时间复杂度是多少?
【发布时间】:2015-08-05 17:43:46
【问题描述】:
def max_k_sort(k, nums):
    # sort nums first using timsort
    # add O(n*log(n)) time complexity
    sorted_nums = sorted(nums)

    return sorted_nums[-1*k:len(nums)]

def max_k(k, nums):
    # build initial max number list
    max_nums = {}

    # add O(k) time complexity?
    i = 0
    while i < k:
        max_nums[i] = 0
        i += 1

    # add O(n) time complexity?
    least_max_key = min(max_nums, key=max_nums.get)
    least_max = max_nums[least_max_key]

    # add O(n) time complexity?
    for n in nums:
        if n > least_max:
            max_nums[least_max_key] = n
            least_max_key = min(max_nums, key=max_nums.get)
            least_max = max_nums[least_max_key]

    return max_nums.values()

print(max_k(5, [2, 8, 4, 9, 0, 12, 12, 6, 5]))

我不太确定这段代码的时间复杂度。任务是从未排序的整数数组中返回最大 k 个数字。数组中的每个数字都在 [0, 10000) 范围内。我的目标是有一个明显的解决方案 max_k_sort(k, nums) 以 O(n*log(n)) 时间复杂度完成任务,另一种方法 max_k(k, nums) 以 O(n) 时间复杂度完成任务其中 n 是传递的整数数量,k 是要查找的最大值的数量。我不禁想知道是否有办法返回按 O(n) 时间复杂度排序的最大值。

【问题讨论】:

  • “我不禁想知道是否有办法返回按 O(n) 时间复杂度排序的最大值。” - 只要您进行比较,就没有骰子。如果有的话,你可以通过 k=n 在 O(n) 时间内对数组进行排序,你无法区分 n! O(n) 比较的可能输入顺序。
  • max_kO(n)?我不太确定。 for 中的 min 呢?
  • 另外,您真的需要按排序顺序排列的最大 k 值,还是只需要最大 k 值?如果您不关心它们的顺序,您可以使用 O(n) selection algorithm 来选择第 k 个最高值,然后构建每个元素的数组 >= 该值。
  • @user2357112,使用选择算法似乎是完美的答案。但是如果我们通过选择算法来解决它,我们没有利用问题陈述给我们的信息,即数字的范围。 “数组中的每个数字都在 [0, 10000) 范围内。”

标签: python algorithm dictionary big-o time-complexity


【解决方案1】:
for n in nums:
        if n > least_max:
            max_nums[least_max_key] = n
            least_max_key = min(max_nums, key=max_nums.get) # this is O(k)
            least_max = max_nums[least_max_key]

您正在执行 O(k) 操作 n 次,因此您的第二个函数的复杂度为 O(n*k)。

假设您希望输出按排序顺序,这可以通过创建一个 k 大小的堆并将所有内容推到它上面,在 O(n*log(k)) 中最容易地完成。这是在heapq.nlargest 中为您实现的。

import heapq

heapq.nlargest(5, [2, 8, 4, 9, 0, 12, 12, 6, 5])
Out[4]: [12, 12, 9, 8, 6]

如果您想要按排序顺序输出,这在技术上可以在 O(n) 中完成。 There exist algorithms(和pythonimplementations)在线性时间内找到数组中第k个最大的元素;很容易看出,再通过一次数组将允许您构建一个所有数字 k 及更大的数组,从而给出总体 O(n)。

【讨论】:

  • "这个运算的数学下限是O(n*log(k))" - 不完全是。它可以在O(n+k*log(k)) 中完成,方法是使用像introselect 这样的选择算法来选择第k 个最高元素,然后构建一个前k 个元素的数组并对其进行排序。如果数组的顺序不重要,我们可以将其降低到O(n)时间。
【解决方案2】:

Pythonstates列表排序中列表操作的时间复杂度为 O(N log N)。

切片是 O(k)

所以:

def max_k(k, nums):
    nums.sort(reverse=True)
    return nums[0:k]

O(k) + O(n log n) 是 O(n log n) 其中 O(k) 小于 O(n log n)

>>> max_k(5, [2, 8, 4, 9, 0, 12, 12, 6, 5])
[12, 12, 9, 8, 6]

实际上,请尝试为它们计时:

import heapq
def max_k1(k, nums):
    nums.sort(reverse=True)
    return nums[0:k]

def max_k2(k, nums):
    return heapq.nlargest(k, nums)    

if __name__ == '__main__':
    import timeit
    for f in (max_k1, max_k2):
        li=[2, 8, 4, 9, 0, 12, 12, 6, 5]
        print f.__name__, timeit.timeit('f(5, li)', setup='from __main__ import f, li')  

打印:

max_k1 0.240165948868
max_k2 4.96488595009

所以 sort 和 slice 比 heapq 快 20 倍。


根据评论:

import heapq
def max_k1(k, nums):
    nums.sort(reverse=True)
    return nums[0:k]

def max_k2(k, nums):
    return heapq.nlargest(k, nums)   

def max_k3(k, nums):
    return sorted(nums, reverse=True)[0:k]    

if __name__ == '__main__':
    import timeit
    for f in (max_k1, max_k2, max_k3):
        li=[2, 8, 4, 9, 0, 12, 12, 6, 5]
        print f.__name__, timeit.timeit('f(5, li)', setup='from __main__ import f, li')    

max_k1 0.242296934128
max_k2 4.52635192871
max_k3 0.332237005234

【讨论】:

  • 我认为测试存在缺陷。 nums.sortnums 进行就地排序。因此,从下一个max_k 开始,nums 将已经排序。尝试使用sorted(nums, reverse=True)[:k]。除此之外,引用文档,The latter two functions(nlargest, nsmallest) perform best for smaller values of n. For larger values, it is more efficient to use the sorted() function. Also, when n==1, it is more efficient to use the built-in min() and max() functions. If repeated usage of these functions is required, consider turning the iterable into an actual heap.
  • 呃,当 n 非常小时,常数因素将主导你的基准测试。当然,当 n=10 时,本机实现的函数会胜出。尝试 n=100000,k=5。
  • @roippi:完全同意。
猜你喜欢
  • 1970-01-01
  • 2013-09-09
  • 1970-01-01
  • 1970-01-01
  • 2018-01-11
  • 1970-01-01
  • 2017-07-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多