非常に大きなファイルで最も多く発生する要素を見つける


12

私はこのインタビューの質問をたくさん聞いたので、良い答えが何であるかについていくつかの意見をもらいたいと思っていました:10GB以上の大きなファイルがあり、どの要素が最も多く発生しているのか、良い方法を見つけたいこれをする?

多くのメモリを使用するため、マップで繰り返し追跡するのはおそらく良い考えではありません。また、エントリが入ってくるときに追跡するのは、この質問が提起されたときにファイルがすでに存在するため、最大のオプションではありません。

ファイルを分割して複数のスレッドで繰り返し処理し、それらの結果を結合することを含めた他の考えもありますが、マップのメモリの問題は依然として残っています。


2
ファイルの要素は何ですか?彼らは文字列ですか?要素に文字を使用する場合、マップにメモリの問題はありません。要素が言葉である場合、再び私はそれが問題にならないと思います。あなたはすべての可能なストリングを持っている場合は、...の問題を持つことができます
Nejc

1
条件が「全要素の半分以上に見える要素」である場合、線形解がありました。
st0le

要素は通常文字列だと思います。しかし、マップがどのように問題になっていないかはわかりません。すべての要素が一意であるという最悪の場合、メモリ要件を2倍にしただけではありませんか?
パット

1
Boyer-Moore多数派候補アルゴリズムが適用可能な場合、線形時間で実行され、インプレースで実行されます。
十宝

回答:


6

あなたが持っているときは、本当に大きなファイルや多くのそれの要素を、最も一般的な要素が非常に一般的である-起こる時間の割合を-あなたは宇宙との線形時間でそれを見つけることができますO K (言葉O 表記の定数は非常に小さく、ハッシュなどの補助的なもののストレージをカウントしない場合は基本的に2です)。さらに、ファイルは一度に1つの要素で順番に処理され、アルゴリズムは決して「振り返る」ことはないため、これは外部ストレージでうまく機能します。これを行う1つの方法は、MisraとGriesによる古典的なアルゴリズムを使用することです。これらの講義ノートを参照してください>1/kO(k)O()。この問題は現在、ヘビーヒッターの問題として知られています(頻繁な要素はヘビーヒッターです)。

前提最も頻繁要素が表示されていることための時間の割合kの小さな数は強いように見えるかもしれませんが、それは必要な方法であります!つまり、ファイルへのシーケンシャルアクセスがある場合(およびファイルが巨大なランダムアクセスの場合は高額になります)、一定のパス数で最も頻繁な要素を常に見つけるアルゴリズムは、要素数に線形のスペースを使用します。したがって、入力について何かを仮定しないと、ハッシュテーブルに勝てません。最も頻繁な要素が非常に頻繁であるという仮定は、おそらく否定的な結果を回避する最も自然な方法です。>1/kk

以下はスケッチです。つまり、半分以上の時間で発生する単一の要素がある場合です。この特別なケースは多数決アルゴリズムとして知られており、ボイヤーとムーアによるものです。単一の要素と単一のカウントを保持します。カウントを1に初期化し、ファイルの最初の要素を保存します。次に、ファイルを順番に処理します。k=2

  • ファイルの現在の要素が保存されている要素と同じである場合、カウントを1つ増やします
  • ファイルの現在の要素が保存されている要素と異なる場合は、カウントを1つ減らします
  • 更新されたカウントが0の場合、保存されている要素を「追い出し」、ファイルの現在の要素を保存します。カウントを1に増やします
  • ファイルの次の要素に進みます

この手順について少し考えてみると、「多数」要素、つまり半分以上の時間で発生する要素が存在する場合、その要素はファイル全体が処理された後の保存された要素になります。

一般の場合、あなたは保つのk - 1つの要素とK - 1つのカウントを、あなたが最初に初期化する要素kのあなたが見る前に、これらの各要素が表示された回数にファイルの異なる要素とカウントをK番目別個の要素。その後、基本的に同じ手順を実行します。検出されるたびに要素のカウントが増加し、保存されていない要素が検出されるとすべての要素カウントが減少し、カウントがゼロの場合、その要素が追い出されますファイルの現在の要素。これはMisra-Griesアルゴリズムです。kk1k1kk

もちろん、ハッシュテーブルを使用して、保存された要素にインデックスを付けることができます。終了時に、このアルゴリズムは、時間の1 / kを超える割合で発生する要素を返すことが保証されています。これは、ファイル上で一定数のパスを作成し、O k ワードのみを保存するアルゴリズムを使用して実行できる最善の方法です。k11/kO(k)

k1/kk1


Boyer-MooreまたはMisra-Gries-Demaineアルゴリズムは使用できません。前述の問題は異なります。多数要素を検索するのではなく、すべての要素の出現回数が> =である要素を検索しています。これは簡単な反例です。ましょうn個の要素の総数は、そのようなものである、N = 2K + 1。最初のk要素を0、次のk要素を1、最後の要素を2とします。ボイヤー・ムーアアルゴリズムは、潜在的な多数候補として最後の要素2を報告します。ただし、この特定のインスタンスでは、出力は0または1のいずれかでなければなりません。
Massimo Cafaro

O(1)Ω(n)

間違った仮定をすると、間違った結果が得られる可能性があることを指摘しました。より良いのは、メモリフットプリントが小さく、潜在的に不正確な結果または正しい結果であるにもかかわらず、メモリがいくらか消費されることです。潜在的に不正確な結果を選択しなければならなかった場合、ボイヤー・ムーアではなく、実際にはそれが本当かどうかわからないことを想定して、ランダム化アルゴリズムを使用します。
マッシモカファロ

@MassimoCafaroそれはあなたが取る必要があるトレードオフではありません。私が指摘したように、ファイル上の単一のパスは、仮定が満たされているかどうかを簡単に検証します!
サショニコロフ

@MassimoCafaroそしてこれは簡単な解決策です!追加のパスなしでCMスケッチを使用すると、仮定を高い確率で検証できます。
サショニコロフ

3

当然の答えは、Nejcが既に提案したように、ハッシュマップを保持し、ファイル内を移動するときに要素の出現のカウンターを格納することです。これが(時間の複雑さの観点から)最適なソリューションです。

ただし、スペース要件が厳しい場合は、 Θ(nlogn).


ハフマンエンコーディングアプローチについて詳しく説明していただけますか?ハフマンエンコーダーを作成したことがありますが、しばらくしてから、この場合にどのように使用しますか?
パット

@Pat Nevermindその部分は朝早くすぎで、どういうわけか入力を圧縮するのが理にかなっていると思いました。
ジャーネイ

1

最も一般的な要素が次の共通要素よりもかなりマージンがあり、ファイルサイズに比べて異なる要素の数が少ない場合、いくつかの要素をランダムにサンプリングし、サンプル内の最も一般的な要素を返すことができます。


また、何度も発生する要素の数が少ない場合は、サンプリングしてそれらを見つけ、それらの要素のみを正確にカウントできます。
最大
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.