これは私が現在取り組んでいる研究プロジェクトの一つです。要件はあなたの要件とほぼ同じであり、問題を解決するための優れたアルゴリズムを開発しました。
入力
入力は、英語の単語またはフレーズの無限のストリームです(それらをと呼びますtokens
)。
出力
- これまでに見た上位N個のトークンを出力します(これまでに見たすべてのトークンから!)
- 過去N日のトークンを履歴ウィンドウに出力します(たとえば、最終日または先週)。
この研究のアプリケーションは、TwitterやFacebookでホットなトピックやトピックのトレンドを見つけることです。ウェブサイトをクロールするクローラーがあり、システムにフィードする単語のストリームを生成します。その後、システムは、全体的または歴史的に、最高頻度の単語またはフレーズを出力します。過去2週間で、「ワールドカップ」というフレーズがTwitterに何度も表示されることを想像してみてください。「タコのポール」もそうです。:)
整数への文字列
システムには、各単語の整数IDがあります。インターネット上にはほぼ無限の可能性のある単語がありますが、大量の単語の集合を蓄積した後、新しい単語を見つける可能性はますます低くなります。すでに400万の異なる単語が見つかり、それぞれに一意のIDが割り当てられています。このデータセット全体をハッシュテーブルとしてメモリに読み込むことができ、約300MBのメモリを消費します。(私たちは独自のハッシュテーブルを実装しました。Javaの実装は巨大なメモリオーバーヘッドを必要とします)
その後、各句は整数の配列として識別できます。
整数の並べ替えと比較は文字列の場合よりもはるかに高速であるため、これは重要です。
データのアーカイブ
システムは、すべてのトークンのアーカイブデータを保持します。基本的にはのペアです(Token, Frequency)
。ただし、データを格納するテーブルは非常に大きいため、テーブルを物理的にパーティション分割する必要があります。パーティションスキームは、トークンのngramに基づいています。トークンが単一の単語の場合、1グラムです。トークンが2語句の場合、2gramです。そして、これは続きます。およそ4グラムには10億のレコードがあり、テーブルのサイズは約60GBです。
着信ストリームの処理
システムは、メモリが完全に利用されるようになるまで(はい、私たちはMemoryManagerが必要です)、着信文を吸収します。N文を取得してメモリに格納した後、システムは一時停止し、各文を単語と語句にトークン化し始めます。各トークン(単語またはフレーズ)がカウントされます。
非常に頻繁なトークンの場合、それらは常にメモリに保持されます。頻度の低いトークンについては、IDに基づいてソートされ(文字列を整数の配列に変換することを忘れないでください)、ディスクファイルにシリアル化されます。
(ただし、問題については、単語のみを数えるので、すべての単語頻度マップをメモリのみに置くことができます。注意深く設計されたデータ構造は、400万の異なる単語に対して300MBのメモリしか消費しません。ヒント:ASCII文字を使用して文字列を表します)、これは十分に受け入れられます。
その間、システムによって生成されたディスクファイルが見つかるとアクティブになる別のプロセスがあり、それをマージし始めます。ディスクファイルはソートされているため、マージはマージソートと同様のプロセスを実行します。ランダムディスクシークが多すぎないようにするため、ここでも注意が必要な設計があります。アイデアは、読み取り(マージプロセス)/書き込み(システム出力)を同時に回避し、マージプロセスで1つのディスクから読み取り、別のディスクに書き込むことです。これは、ロックの実装に似ています。
一日の終わり
1日の終わりに、システムには頻繁にトークンがメモリに保存され、頻度の低い他の多くのトークンがいくつかのディスクファイルに保存されます(各ファイルがソートされます)。
システムは、メモリ内マップをディスクファイルにフラッシュします(ソートします)。今、問題はソートされたディスクファイルのセットをマージすることになります。同様のプロセスを使用して、最後に1つのソートされたディスクファイルを取得します。
次に、最後のタスクは、ソートされたディスクファイルをアーカイブデータベースにマージすることです。アーカイブデータベースのサイズによって異なりますが、十分に大きい場合、アルゴリズムは次のように機能します。
for each record in sorted disk file
update archive database by increasing frequency
if rowcount == 0 then put the record into a list
end for
for each record in the list of having rowcount == 0
insert into archive database
end for
直感的には、しばらくすると挿入数がどんどん少なくなっていきます。ますます多くの操作が更新のみになります。そして、この更新はインデックスによってペナルティが課されることはありません。
この説明全体がお役に立てば幸いです。:)
what is the most frequent item in the subsequence [2; 2; 3; 3; 3; 4; 4; 4; 4; 5; 5] of your sequence?