maxがsortより遅いのはなぜですか?


92

Python 2および3の関数maxよりも遅いことがわかりましたsort

Python 2

$ python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'a.sort();a[-1]'
1000 loops, best of 3: 239 usec per loop
$ python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'max(a)'        
1000 loops, best of 3: 342 usec per loop

Python 3

$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'a.sort();a[-1]'
1000 loops, best of 3: 252 usec per loop
$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'max(a)'
1000 loops, best of 3: 371 usec per loop

()が関数()より遅いの なぜですか?maxO(n)sortO(nlogn)


3
Python 2分析を1回実行しましたが、Python 3コードはまったく同じです。
16年

9
a.sort()インプレースで動作します。試してみるsorted(a)
Andrea Corbellini、2016年

修正した場合は、修正のために行ったことを投稿してください。
プレッツェル2016年

4
@Pretzel OPは、問題が修正されたことではなく、投稿が編集されたことを意味します。
16年

2
@WeizhongTuがsortソートaされますが、その後永久にソートされます
njzk2

回答:


125

timeitPythonでモジュールを使用する場合は、十分に注意する必要があります。

python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'a.sort();a[-1]'

ここでは、初期化コードが1回実行され、ランダム化された配列が生成されaます。次に、残りのコードが数回実行されます。初めて配列をソートするときは、2回おきに、すでにソートされた配列に対してsortメソッドを呼び出します。返されるのは最速の時間だけなので、実際には、Pythonが既に並べ替えられた配列を並べ替えるのにかかる時間を計っています。

Pythonのソートアルゴリズムの一部は、配列がすでに部分的または完全にソートされていることを検出することです。完全にソートされたら、アレイを1回スキャンしてこれを検出し、停止します。

代わりにあなたが試した場合:

python -m timeit -s 'import random;a=range(100000);random.shuffle(a)' 'sorted(a)[-1]'

次に、すべてのタイミングループで並べ替えが行われ、配列の並べ替えにかかる時間が実際には最大値を見つけるよりもはるかに長いことがわかります。

編集: @skykingの答えは私が説明できなかった部分を説明しています:a.sort()それが要素に直接アクセスできるようにリストで動作していることを知っています。max(a)任意の反復可能オブジェクトで機能するため、一般的な反復を使用する必要があります。


10
良いキャッチ。インタプリタの状態がコードの実行全体で保持されることに気づきませんでした。今、私は過去にいくつの欠陥のあるベンチマークを作成したのでしょうか。:-}
Frerich Raabe

1
それは私には明らかでした。しかし、すでにソートされた配列をソートする場合でも、すべての要素をチェックする必要があることに注意してください。これは、最大を取得するのと同じくらい多くの作業です...私にとっては、これは半分答えのように見えます。
Karoly Horvath

2
@KarolyHorvath、あなたは正しいです。私は@skykingが答えのもう半分を得たと思います:a.sort()リストに取り組んでいるので、要素に直接アクセスできることを知っています。max(a)任意のシーケンスで機能し、一般的な反復を使用しません。
ダンカン

1
@KarolyHorvath多分分岐予測は、ソートされた配列を繰り返しソートする方が速い理由を説明できます:stackoverflow.com/a/11227902/4600
marcospereira

1
@JuniorCompressor listsort.txtは、「部分的に順序付けられた多くの種類の配列(必要なlg(N!)の比較より少なく、N-1と同じくらい少ない)で超自然なパフォーマンスを発揮します」を説明し、あらゆる種類の残酷な最適化について説明します。私はそれがmaxできない多くの仮定をすることができると思います、すなわちソートは漸近的に速くありません。
Frerich Raabe

87

まず、max()はイテレータプロトコル使用し、list.sort()アドホックコード使用ていることに注意しください。明らかに、イテレータを使用することは重要なオーバーヘッドであるため、タイミングの違いを観察しています。

ただし、それとは別に、テストは公平ではありません。a.sort()同じリストで複数回実行しています。Pythonが使用するアルゴリズムは、具体的には、既に(部分的に)データをソートするために高速であるように設計されています。あなたのテストは、アルゴリズムがうまく機能していると言っています。

これらは公正なテストです:

$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'max(a[:])'
1000 loops, best of 3: 227 usec per loop
$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'a[:].sort()'
100 loops, best of 3: 2.28 msec per loop

ここでは、毎回リストのコピーを作成しています。ご覧のとおり、結果の大きさの順序は異なります。予想どおり、マイクロ秒とミリ秒です。

そして覚えておいてください:big-Ohは上限を指定します!Pythonのソートアルゴリズムの下限はΩ(n)です。O(n log n)であっても、すべての実行にn log nに比例した時間がかかるとは限りません。これは、O(n)アルゴリズムよりも低速である必要があることを意味することさえありませんが、それは別の話です。理解しておくべき重要なことは、いくつかの好ましいケースでは、O(n log n)アルゴリズムがO(n)時間以下で実行される可能性があることです。


31

これl.sortは、listwhileのメンバーがmaxジェネリック関数であるためです。これはl.sortlistwhile の内部表現に依存できるため、max汎用のイテレータプロトコルを経由する必要があることを意味します。

これにより、各要素のフェッチは、各要素のフェッチl.sortよりも高速になりますmax

代わりに使用したsorted(a)場合、結果はより遅くなると思いますmax(a)


5
その仮定は、より具体的になるためのタイミングのほんの一部です。あなたの知識に疑問を投げかけるのではなく、そのような追加はそれを知らない人たちのデモンストレーションにとって取るに足らないことです。
Reti43

あなたはそれが正しいだsorted(a)より遅いですmax(a)。当然のことながら、それはとほぼ同じ速度ですがa.sort()、そうではない理由についてのあなたの推測-それは、OPが受け入れられた回答で指摘されているように、テストでミスを犯したためです。
martineau

要点は、汎用イテレータプロトコルにはlog(n)、複雑さの要因を相殺するのに十分なオーバーヘッドがある可能性があるということです。つまり、O(n)アルゴリズムはO(nlogn)、十分に大きい場合のアルゴリズムよりも高速であることが保証されているだけですn(たとえば、各操作の時間がアルゴリズム間で異なる可能性があるため- nlogn高速ステップはn低速ステップよりも速い場合があります)。この場合、損益分岐点が正確にどこにあるかは考慮されませんでした(ただし、log n要素がsmallishの場合、それほど大きな要素ではないことに注意してくださいn)。
skyking
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.