Python でソートされた一意のリストを取得する最も速い方法は何ですか? 質問する

Python でソートされた一意のリストを取得する最も速い方法は何ですか? 質問する

Python でソートされた一意のリストを取得する最も高速な方法は何ですか? (ハッシュ可能なもののリストがあり、反復処理できるものが必要です。リストがインプレースで変更されるか、新しいリストを取得するか、反復可能なリストを取得するかは関係ありません。私の具体的な使用例では、使い捨てリストを使用してこれを実行しているため、インプレースの方がメモリ効率が高くなります。)

私は次のような解決策を見てきました

input = [5, 4, 2, 8, 4, 2, 1]
sorted(set(input))

しかし、最初に一意性をチェックしてからソートするのは無駄に思えます(リストをソートするときには、基本的に挿入ポイントを決定する必要があり、その結果、副作用として一意性テストが発生するためです)。おそらく、UNIXの

cat list | sort | uniq

すでにソートされたリスト内の連続した重複を選択するだけですか?


質問の「Pythonでリストを一意にする最も速い方法' リストはソートされておらず、 'Python リストで sort と uniq を実行する最もクリーンな方法は何ですか?' は最もクリーンで Python らしい方法を求めており、受け入れられた回答では が提案されていますがsorted(set(input))、これを改善しようとしています。

ベストアンサー1

sorted(set(sequence))これが最も速い方法だと思います。確かsetにシーケンスを反復しますが、これはCレベルのループであり、たくさんPython レベルで実行するループよりも高速です。

ただし、それでもgroupbyまだ実行できO(n) + O(nlogn) = O(nlogn)、最悪なのは、groupbyPython レベルのループが必要になるため、定数が大幅に増加し、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

ご覧の通り、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

ほぼ 5 倍遅くなります。seq.sort()と を使用してから とmake_unique(seq)を使用することはmake_unique(sorted(seq))実際には同じであることに注意してください。Timsort はO(n)スペースを使用するため、常にいくらかの再割り当てが行われ、 を使用してもsorted(seq)実際にはタイミングはあまり変わりません。

jdm のベンチマークでは、使用している入力が小さすぎるため、すべての時間が呼び出しに費やされ、異なる結果が得られますtime.clock()

おすすめ記事