膨大な数の数字から最大の数字を引き出す方法は?
質問
少なくとも100000000個の数字のリストから最大100個の要素を取得したい。
リスト全体をソートし、ソートされたリストから最後の100個の要素を取得することもできますが、メモリと時間の両方の面で非常にコストがかかります。
これを行うための既存の簡単な、python的な方法はありますか?
私が欲しいのは、純粋なソートの代わりに次の関数です。実際、気にしない要素をソートするために時間を無駄にしたくありません。
たとえば、これは私が持ちたい機能です:
getSortedElements(100, lambda x,y:cmp(x,y))
この要件はパフォーマンスの観点のみに注意してください。
解決
標準ライブラリのheapqモジュールには、これを行うためのnlargest()関数が用意されています。
top100 = heapq.nlargest(100, iterable [,key])
リスト全体をソートするわけではないため、不要な要素に時間を浪費することはありません。
他のヒント
選択アルゴリズムがここで役立ちます。
非常に簡単な解決策は、100番目に大きい要素を見つけてから、この要素より大きい要素を選択してリストを実行することです。それはあなたに100の最大の要素を提供します。これはリストの長さが直線的です。これが最良の方法です。
より洗練されたアルゴリズムがあります。たとえば、ヒープは、この問題に非常に適しています。ヒープベースのアルゴリズムは n log k
です。ここで、 n
はリストの長さ、 k
は選択する最大要素の数です。 。
この問題については、選択アルゴリズムの説明があります。
編集:別のポスターは、Pythonにはこの問題に対する組み込みのソリューションがあることを指摘しています。明らかに、それはあなた自身のものを転がすよりはるかに簡単ですが、そのようなアルゴリズムがどのように機能するかについて学びたい場合に備えて、この投稿を続けます。
ヒープデータ構造を使用できます。ヒープは必ずしも順序付けられるとは限りませんが、半順序のデータを保持するためのかなり高速な方法であり、常に最小のアイテムがヒープの最初の要素になるという利点があります。
ヒープには、追加と置換の2つの基本操作があります。
基本的には、100個のアイテム(質問ごとの上位N個の番号)に達するまでアイテムを追加します。その後、新しいアイテムが最初のアイテムよりも大きい限り、最初のアイテムをすべての新しいアイテムに置き換えます。
最初のアイテムをより大きなものに置き換えるたびに、ヒープ内の内部コードはヒープの内容を調整し、新しいアイテムが最小ではない場合、ヒープにバブルアップし、最小のアイテムが"バブルダウン"途中で交換する準備ができている最初の要素に。
これを行う最良の方法は、ヒープに並べ替えられた優先度キューを維持することです。この優先度キューには、100個のエントリが含まれたらポップオフします。
結果がソートされているかどうかは気にしませんが、これは直感的に無料で入手できます。あなたがトップ100であることを知るには、いくつかの効率的なデータ構造を介して、トップ番号の現在のリストを順番に並べる必要があります。その構造は、各要素の最小値、最大値、および相対的な位置を、何らかの自然な方法で知っているので、隣接する要素の隣に位置をアサートできます。
pythonで述べたように、heapqを使用します。 Java PriorityQueueの場合: http://java.sun.com/javase/ 6 / docs / api / java / util / PriorityQueue.html
これは、ライブラリから独立した、私が使用したソリューションです。 配列を持つすべてのプログラミング言語で動作します:
初期化:
Make an array of 100 elements and initialise all elements
with a low value (less than any value in your input list).
Initialise an integer variable to 0 (or any value in
[0;99]), say index_minvalue, that will point to the
current lowest value in the array.
Initialise a variable, say minvalue, to hold the current
lowest value in the array.
入力リストの各値、たとえばcurrent_valueについて:
if current_value > minvalue
Replace value in array pointed to by index_minvalue
with current_value
Find new lowest value in the array and set index_minvalue to
its array index. (linear search for this will be OK as the array
is quickly filled up with large values)
Set minvalue to current_value
else
<don't do anything!>
minvalueはすぐに高い値を取得するため、ほとんどの値は 入力リスト内の最小値と比較する必要があるだけです (比較の結果はほとんどfalseになります)。
聴衆のアルゴリズムの場合:Tony Hoareのアルゴリズムの簡単なバリエーションでこれを行うことができます 検索 :
find(topn, a, i, j)
pick a random element x from a[i..j]
partition the subarray a[i..j] (just as in Quicksort)
into subarrays of elements <x, ==x, >x
let k be the position of element x
if k == 0 you're finished
if k > topn, call find(topn, a, i, k)
if k < topn, call find(topn-k, k, j)
このアルゴリズムは、最大の topn
要素を配列 a
の最初の topn
要素に、せずに並べ替えます。もちろん、それらをソートしたい場合、または単純化するために、ヒープの方が優れており、ライブラリ関数の呼び出しはさらに優れています。しかし、それはクールなアルゴリズムです。