web-dev-qa-db-ja.com

maxがソートより遅いのはなぜですか?

maxは、Python 2および3の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
_

なぜismaxO(n))はsort関数(O(nlogn))よりも遅いのですか?

91
WeizhongTu

Pythonでtimeitモジュールを使用するときは、非常に注意する必要があります。

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

ここでは、初期化コードが1回実行され、ランダム化された配列aが生成されます。その後、残りのコードが数回実行されます。初めて配列をソートしますが、それ以外の場合は、すでにソートされた配列でsortメソッドを呼び出しています。最速の時間のみが返されるため、実際には、すでに並べ替えられた配列を並べ替えるのに要する時間Python.

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

代わりに試した場合:

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

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

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

124
Duncan

まず、 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

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

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

88

これは、_l.sort_がlistのメンバーであり、maxがジェネリック関数であるためです。これは、_l.sort_がlistの内部表現に依存できる一方で、maxは汎用イテレータプロトコルを通過する必要があることを意味します。

これにより、_l.sort_の各要素フェッチは、maxが行う各要素フェッチよりも高速になります。

代わりにsorted(a)を使用すると、max(a)よりも結果が遅くなると思います。

31
skyking