在python中获取排序唯一列表的最快方法?

jdm*_*jdm 20 python sorting optimization

在python中获取排序,唯一列表的禁区方法是什么?(我有一个可清洗的东西列表,并希望有一些我可以迭代的东西 - 无论列表是否被修改到位,或者我得到一个新的列表,或者是一个可迭代的.在我的具体用例中,我'使用一次性列表执行此操作,因此就可以提高内存效率.)

我见过类似的解决方案

input = [5, 4, 2, 8, 4, 2, 1]
sorted(set(input))
Run Code Online (Sandbox Code Playgroud)

但在我看来,首先检查唯一性然后排序是浪费的(因为当您对列表进行排序时,您基本上必须确定插入点,因此将唯一性测试作为副作用).也许还有更多类似于unix的东西

cat list | sort | uniq
Run Code Online (Sandbox Code Playgroud)

只是在已经排序的列表中选择连续重复?


请注意" 在Python中使用最快化的列表方法 "这个问题 ,列表没有排序,并且' 在Python列表中进行排序加uniq的最简洁方法什么?'要求最干净/最pythonic的方式,并且接受的答案表明sorted(set(input)),我正在努力改进.

Bak*_*riu 25

我相信这sorted(set(sequence))是最快的做法.是的,set在序列迭代,但是这是一个C级循环,这是很多比你的循环将在蟒蛇的水平做更快.

请注意,即使groupby你仍然拥有O(n) + O(nlogn) = O(nlogn)并且最糟糕的是groupby需要一个python级别的循环,这会大大增加其中的常量,O(n)从而最终获得最差的结果.

在谈到CPython时,优化事物的方法就是在C级别尽可能多地做到(请参阅答案以获得另一个反直觉性能的示例).要获得更快的解决方案,您必须在C扩展中重新实现排序.即便如此,祝你获得像python的Timsort一样快的东西!

"规范解决方案"与groupby解决方案的小比较:

>>> import timeit
>>> sequence = list(range(500)) + list(range(700)) + list(range(1000))
>>> timeit.timeit('sorted(set(sequence))', 'from __main__ import sequence', number=1000)
0.11532402038574219
>>> import itertools
>>> def my_sort(seq):
...     return list(k for k,_ in itertools.groupby(sorted(seq)))
... 
>>> timeit.timeit('my_sort(sequence)', 'from __main__ import sequence, my_sort', number=1000)
0.3162040710449219
Run Code Online (Sandbox Code Playgroud)

你可以看到它慢了3倍.

jdm提供的版本实际上更糟糕:

>>> def make_unique(lst):
...     if len(lst) <= 1:
...         return lst
...     last = lst[-1]
...     for i in range(len(lst) - 2, -1, -1):
...         item = lst[i]
...         if item == last:
...             del lst[i]
...         else:
...             last = item
... 
>>> def my_sort2(seq):
...     make_unique(sorted(seq))
... 
>>> timeit.timeit('my_sort2(sequence)', 'from __main__ import sequence, my_sort2', number=1000)
0.46814608573913574
Run Code Online (Sandbox Code Playgroud)

慢了近5倍.请注意,使用seq.sort()然后make_unique(seq)make_unique(sorted(seq))实际上是相同的事情,因为Timsort使用O(n)空间总是有一些重新分配,所以使用sorted(seq)实际上并没有改变太多的时间.

jdm的基准测试给出了不同的结果,因为他使用的输入太小,因此所有时间都是由time.clock()调用完成的.


kas*_*sky 5

也许这不是您正在寻找的答案,但无论如何,您应该考虑到这一点。

基本上,您在列表上有 2 个操作:

unique_list = set(your_list)       # O(n) complexity
sorted_list = sorted(unique_list)  # O(nlogn) complexity
Run Code Online (Sandbox Code Playgroud)

现在,你说“在我看来,首先检查唯一性然后排序是浪费的”,你是对的。但是,这个多余的步骤到底有多糟糕?取 n = 1000000:

# sorted(set(a_list))
O(n) => 1000000
o(nlogn) => 1000000 * 20 = 20000000
Total => 21000000

# Your fastest way
O(nlogn) => 20000000
Total: 20000000
Run Code Online (Sandbox Code Playgroud)

速度增益:(1 - 20000000/21000000) * 100 = 4.76 %

对于 n = 5000000,速度增益:~1.6 %

现在,这种优化值得吗?