上位10の検索用語を見つけるアルゴリズム


115

私は現在インタビューの準備をしていますが、前回のインタビューで一度尋ねられた質問を思い出しました。

「Googleで上位10の検索語を継続的に表示するソフトウェアを設計するように求められました。Googleで現在検索されている検索語の無限のリアルタイムストリームを提供するフィードへのアクセス権が与えられます。どのアルゴリズムとデータ構造を説明してくださいこれを実装するには、次の2つのバリエーションを設計します。

(i)すべての時間(つまり、フィードの読み取りを開始してから)の上位10の検索語を表示します。

(ii)毎月更新された、過去1か月の上位10の検索用語のみを表示します。

概算を使用して上位10のリストを取得できますが、選択を正当化する必要があります。」
私はこのインタビューで爆破しましたが、これを実装する方法はまだまったくわかりません。

最初の部分では、無限リストの継続的に成長するサブシーケンスで最も頻繁に使用される10個のアイテムを要求します。選択アルゴリズムを調べましたが、この問題を解決するためのオンラインバージョンが見つかりませんでした。

2番目の部分では有限のリストを使用しますが、大量のデータが処理されるため、1か月分の検索語をメモリに格納して1時間ごとにヒストグラムを計算することはできません。

トップ10リストが継続的に更新されているため、問題はさらに困難になっているため、スライディングウィンドウでトップ10を計算する必要があります。

何か案は?


11
@BlueRaja-それは愚かなインタビューの質問ではありません、それはOP側の悪い解釈です。それは、無限リスト内の最も頻繁なアイテムを求めているのではなく、無限リストの有限サブシーケンスの最も頻繁なアイテムを求めています。アナロジーを続けるためにwhat is the most frequent item in the subsequence [2; 2; 3; 3; 3; 4; 4; 4; 4; 5; 5] of your sequence?
IVlad

3
@BlueRaja-これは確かに難しい質問ですが、なぜそれが愚かであるのかはわかりません。これは、巨大なデータセットを抱える企業が直面しているかなり典型的な問題を代表しているようです。@IVlad-あなたの提案に従ってそれを修正しました、私の側の悪い言葉遣い!
デル

回答:


47

まあ、すべての周波数を格納するための多分法外なコストで、ひどい大量のデータのように見えます。データの量が多すぎてすべてを格納することができない場合は、データストリームアルゴリズムの領域に入ります

この分野で役立つ本: Muthukrishnan-"Data Streams:Algorithms and Applications"

私が上記から選んだ当面の問題への密接に関連した参照: Manku、Motwani-"データストリーム上のおおよその周波数カウント" [pdf]

ちなみに、スタンフォードのMotwani(編集)は、非常に重要な「ランダム化アルゴリズム」の本の著者でした。この本の第11章はこの問題を扱います編集:すみません、悪い参照、その特定の章は別の問題にあります。チェックした後、代わりにオンラインで入手できるMuthukrishnanの本のセクション5.1.2を勧めます。

えっと、いいインタビューの質問です。


2
+1非常に興味深いもの。サイトに「読む」ものをタグ付けする方法があるはずです。共有いただきありがとうございます。
Ramadheer Singh 2010

@Gollum:ブックマークにto-readフォルダがあります。あなたはそれを行うことができます。私はそれらのリンクが私のものに追加されていることを知っています:)
Cam

+1。ストリーミングアルゴリズムはまさにここのトピックであり、Muthuの本(これまでに書かれた唯一の本、AFAIK)は素晴らしいです。
ShreevatsaR 2010

1
+1。関連:en.wikipedia.org/wiki/Online_algorithm。ところで、Motwaniはので、おそらく、最近亡くなっ著者が、より正確です。

非常に奇妙な。私は彼を本から知っていましたが、彼はおそらくこれによりもっと有名だったに違いありません。「モトワニは、PageRankアルゴリズムに関する影響力のある初期の論文の共著者(Larry PageとSergey Brin、およびTerry Winogradの一人) Googleの検索技術の基礎」(。en.wikipedia.org/wiki/Rajeev_Motwani
ディミトリスAndreou

55

周波数推定の概要

一定量のストレージを使用して、そのようなストリームの周波数推定を提供できるいくつかのよく知られたアルゴリズムがあります。1つは Misra and Gries(1982)よるFrequentです。n個の項目のリストから、k-1カウンターを使用して、n / k回以上発生するすべての項目を見つけます。これは、ボイヤーとムーアのマジョリティアルゴリズム(Fischer-Salzberg、1982)の一般化です。ここで、kは2 です。MankuとMotwaniのLossyCounting(2002)とMetwallyのSpaceSaving(2005)のアルゴリズムは、同様のスペース要件を持っていますが、一定の下で、より正確な推定値を提供することができます条件。

覚えておくべき重要なことは、これらのアルゴリズムは周波数推定値しか提供できないことです。具体的には、Misra-Griesの推定は、実際の頻度を(n / k)だけ過小評価する可能性があります項目ます。

50%を超えて発生した場合にのみアイテムを明確に識別できるアルゴリズムがあるとします。このアルゴリズムにN個の異なるアイテムのストリームを供給し、次に、1つのアイテムxの別のN-1コピーを追加して、合計2N-1アイテムにします。アルゴリズムによって、xが合計の50%を超えていると通知された場合、それは最初のストリームにあったはずです。そうでない場合、xは最初のストリームにありませんでした。アルゴリズムがこの決定を行うためには、初期ストリーム(またはその長さに比例するいくつかの要約)を格納する必要があります!したがって、そのような「正確な」アルゴリズムで必要なスペースはΩ(N)であることを自分で証明できます。

代わりに、ここで説明するこれらの頻度アルゴリズムは推定値を提供し、しきい値を超えるアイテムを特定のマージンで下回るいくつかのアイテムとともに識別します。たとえば、単一のカウンタを使用するマジョリティアルゴリズムは常に結果を出します。ストリームの50%を超えるアイテムがある場合、そのアイテムが見つかります。しかし、それはまたあなたに一度だけ起こるアイテムを与えるかもしれません。データに対して2回目のパスを作成しないとわかりません(ここでも、単一のカウンターを使用しますが、その項目のみを探します)。

頻繁なアルゴリズム

Misra-GriesのFrequentアルゴリズムの簡単な説明を次に示します。Demaine(2002)などはアルゴリズムを最適化していますが、これにより要点がわかります。

しきい値の割合1 / kを指定します。n / k回より多く発生するアイテムが見つかります。空のマップ(赤黒木のような)を作成します。キーは検索語であり、値はその語のカウンターになります。

  1. ストリームの各アイテムを見てください。
  2. 用語がマップに存在する場合、関連するカウンターを増分します。
  3. それ以外の場合、マップがk-1エントリー未満の場合は、カウンターを1にして用語をマップに追加します。
  4. ただし、マップに既にk-1のエントリがある場合は、すべてのエントリのカウンターをデクリメントします。このプロセス中にいずれかのカウンターがゼロに達した場合は、マップから削除します。

固定量のストレージ(固定サイズのマップのみ)を使用して、無限量のデータを処理できることに注意してください。必要なストレージの量は対象のしきい値にのみ依存し、ストリームのサイズは重要ではありません。

検索のカウント

このコンテキストでは、おそらく1時間の検索をバッファリングし、その1時間のデータに対してこのプロセスを実行します。この1時間の検索ログで2番目のパスを取得できる場合、最初のパスで特定された上位の「候補」の正確な発生数を取得できます。または、1つのパスを作成し、すべての候補者を報告して、そこにあるはずのアイテムが含まれていること、および追加のアイテムは次の1時間で消える単なるノイズであることを知っていてもよいでしょう。

関心のあるしきい値を本当に超えている候補者は、要約として保存されます。1か月分のこれらの要約を保存し、最も古いものを1時間ごとに破棄します。そうすれば、最も一般的な検索用語を適切に近似できます。


このソリューションはフィルターとして機能し、関心のある検索語の数を減らすことができると思います。用語がマップに含まれる場合、マップから外れた場合でも、実際の統計の追跡を開始します。次に、データの2番目のパスをスキップして、収集した限られた統計からソートされた上位10件を生成できます。
ドルフ

カウンターをデクリメントすることで、ツリーからあまり検索されない用語を削除するエレガントな方法が好きです。しかし、マップが「完全」になったら、到着するすべての新しい検索用語に対してデクリメントステップが必要ではないでしょうか。そして、これが発生し始めたら、カウンターが十分に増加する機会が得られる前に、新しい検索用語がマップからすぐに削除されないのでしょうか?
デル

1
@del-このアルゴリズムは、指定されたしきい値頻度を超える用語を見つけるためのものであり、必ずしも最も一般的な用語を見つけるためのものではないことに注意してください。最も一般的な用語が指定されたしきい値を下回ると、通常は見つかりません。新しい用語を「速すぎて」削除することに関する懸念は、このケースに関連している可能性があります。これを見る1つの方法は、人気のある実際の「シグナル」があり、それらが「ノイズ」から著しく目立つことです。ただし、ランダムな検索が静的であるだけで、信号が見つからない場合もあります。
エリクソン2010

@erickson-正しい-私が得ているのは、このアルゴリズムの前提は、上位10ワードが測定ウィンドウ全体に均一に分布しているということです。ただし、測定ウィンドウを十分に小さく(たとえば1時間)保つ限り、これはおそらく有効な仮定です。
デル

1
@erickson、均一な分布は必須ではありませんが、これがより現実的な分布(power-law、Zipf)でどのように機能するのか疑問に思います。N個の異なる単語があると仮定して、容量Kの赤黒ツリーを保持します。最終的にKの最も頻繁な用語になることを期待します。(N-K)ワードの用語の累積頻度がK個の最も頻度の高いワードの累積頻度よりも大きい場合、最後のツリーにはゴミが含まれることが保証されます。同意しますか?
Dimitris Andreou 2010

19

これは私が現在取り組んでいる研究プロジェクトの一つです。要件はあなたの要件とほぼ同じであり、問​​題を解決するための優れたアルゴリズムを開発しました。

入力

入力は、英語の単語またはフレーズの無限のストリームです(それらをと呼びますtokens)。

出力

  1. これまでに見た上位N個のトークンを出力します(これまでに見たすべてのトークンから!)
  2. 過去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

直感的には、しばらくすると挿入数がどんどん少なくなっていきます。ますます多くの操作が更新のみになります。そして、この更新はインデックスによってペナルティが課されることはありません。

この説明全体がお役に立てば幸いです。:)


わかりません。単語の整数IDで、どのような意味のあるソートまたは比較ができるでしょうか?数字は恣意的ではありませんか?
Dimitris Andreou 2010

また、単語の頻度を数えることは、GoogleのMapReduceペーパー(labs.google.com/papers/mapreduce.html)の最初の例であり、数行でスケーラブルに解決します。データをgoogle app angineに移動して、そのようなMapReduce(code.google.com/p/appengine-mapreduce)を実行することもできます
Dimitris Andreou

@Dimitris Andreou:整数でのソートは文字列で高速になります。これは、2つの整数を比較する方が2つの文字列を比較するよりも速いためです。
SiLent SoNG 2010

@Dimitris Andreou:Googleのmapreduceは、この問題を解決するための優れた分散型アプローチです。ああ!リンクを提供してくれてありがとう。ええ、複数のマシンを使用してソートするのは良いことです。素敵なアプローチ。
SiLent SoNG 2010

@Dimitris Andreou:これまでのところ、私は単一マシンのソート方法のみを検討していました。ディストリビューションを分類するのに、なんといい考えでしょう。
SiLent SoNG 2010

4

バイナリ検索ツリーと組み合わせたハッシュテーブルを使用できます。各検索語句が何回検索されたかを示す辞書を実装します。<search term, count>

ハッシュテーブル全体を1時間ごとに繰り返して上位10を取得するのは明らか悪いことです。しかし、これはGoogleが話しているGoogleであるため、トップ10はすべて、たとえば10 000ヒットを超えると考えられます(ただし、はるかに大きな数になります)。したがって、検索語句の数が10 000を超えるたびに、BSTに挿入します。その後、1時間ごとに、BSTから最初の10を取得するだけで済みます。BSTには比較的少ないエントリが含まれているはずです。

これは、常にトップ10の問題を解決します。


本当にトリッキーな部分は、月次レポートで1つの用語が別の用語に取って代わることです(たとえば、「スタックオーバーフロー」は過去2か月で50 000ヒットする可能性がありますが、「アマゾン」は40過去2か月は000、過去1か月は3万。月次レポートでは、「amazon」を「スタックオーバーフロー」の前に置きたい)。これを行うには、すべての主要な(1万回を超える検索)検索用語について、その用語が毎日何回検索されたかを示す30日間のリストを保存します。リストはFIFOキューのように機能します。最初の日を削除し、新しい日を毎日(または毎時間)挿入しますが、より多くの情報を保存する必要がある場合があります。つまり、より多くのメモリ/スペースが必要になります。それ以外の場合は、その「近似」に進みます

これは良いスタートのようです。次に、1万回以上ヒットしたが、長い間多くはなかった用語やそのような用語の剪定について心配することができます。


3

ケースi)

すべての検索用語のハッシュテーブルと、ハッシュテーブルとは別にソートされた上位10個のリストを維持します。検索が発生するたびに、ハッシュテーブルの適切なアイテムをインクリメントし、そのアイテムが上位10個のリストの10番目のアイテムに切り替わるかどうかを確認します。

トップ10リストのO(1)ルックアップ、およびハッシュテーブルへの最大O(log(n))挿入(自己分散バイナリツリーによって管理される衝突を想定)。

ケースii) 巨大なハッシュテーブルと小さなリストを維持する代わりに、すべてのアイテムのハッシュテーブルとソートされたリストを維持します。検索が行われるたびに、その用語はハッシュテーブルでインクリメントされます。ソートされたリストでは、用語をチェックして、その後の用語に切り替える必要があるかどうかを確認できます。セルフバランシングバイナリツリーは、すばやくクエリできるようにする必要があるため、これに適しています(これについては後で詳しく説明します)。

さらに、FIFOリスト(キュー)の形式で「時間」のリストも維持します。各「時間」要素には、その特定の時間内に行われたすべての検索のリストが含まれます。たとえば、時間のリストは次のようになります。

Time: 0 hours
      -Search Terms:
          -free stuff: 56
          -funny pics: 321
          -stackoverflow: 1234
Time: 1 hour
      -Search Terms:
          -ebay: 12
          -funny pics: 1
          -stackoverflow: 522
          -BP sucks: 92

次に、1時間ごと:リストに少なくとも720時間(30日の時間数)がある場合は、リストの最初の要素を調べ、検索語ごとに、ハッシュテーブルのその要素を適切な量だけ減らします。 。その後、最初の1時間の要素をリストから削除します。

それでは、721時間で、リスト(上記)の最初の1時間を確認する準備ができたとします。ハッシュテーブルで無料のものを56減らし、面白い写真を321で減らします。その後、再び見る必要がないため、リストから時間0を完全に削除します。

高速クエリを可能にするすべての用語の並べ替えられたリストを維持する理由は、720時間前の検索用語を処理した後、毎時間、上位10のリストが並べ替えられたままであることを確認する必要があるためです。したがって、たとえばハッシュテーブルで「無料のもの」を56だけデクリメントするときに、リストのどこに現在属しているかを確認します。これは自己平衡型のバイナリツリーであるため、O(log(n))時間ですべてをうまく実行できます。


編集:スペースの精度を犠牲にしています...

2番目のリストのように、最初のリストにも大きなリストを実装すると便利な場合があります。次に、両方の場合に次のスペース最適化を適用できます。cronジョブを実行して、リストの上位xアイテム以外をすべて削除します。これにより、必要な領域が抑えられます(その結果、リストに対するクエリが速くなります)。もちろん、おおよその結果になりますが、これは許可されています。xは、使用可能なメモリに基づいてアプリケーションをデプロイする前に計算され、より多くのメモリが使用可能になった場合に動的に調整されます。


2

大まかな考え...

常にトップ10に

  • 各用語のカウントが保存されているハッシュコレクションの使用(用語のサニタイズなど)
  • 用語のカウントが配列の最小カウント以上になるたびに、進行中の上位10、用語/カウントがこの配列に追加されるソートされた配列

毎月更新される毎月のトップ10の場合:

  • 744を法とする開始からの経過時間数(1か月の時間数)でインデックス付けされた配列を使用します。この配列エントリは、この時間スロットで発生した各用語のカウントが格納されるハッシュコレクションで構成されます。エントリは、時間スロットカウンターが変更されるたびにリセットされます
  • 時間スロットにインデックスが付けられたこの配列の内容をコピーしてフラット化することにより、時間スロットにインデックスが付けられた配列の統計を、現在の時間スロットカウンターが変更されるたびに(最大で1時間に1回)収集する必要があります。

エラー...理にかなっていますか?私はこれを現実の生活のように考えていませんでした

ああ、そうです、毎月の統計に必要な1時間ごとの「コピー/フラット化」は、実際にすべての時間の上位10に使用されたものと同じコードを再利用できます。これは素晴らしい副作用です。


2

正確なソリューション

まず、正しい結果を保証するが、大量のメモリ(大きなマップ)を必要とするソリューション。

「常時」バリアント

キーとしてのクエリと値としてのカウントを含むハッシュマップを維持します。さらに、これまでに最も頻繁に実行された10個のクエリのリストと、10番目に頻繁に発生するカウント(しきい値)のリストを保持します。

クエリのストリームが読み取られるときに、常にマップを更新します。カウントが現在のしきい値を超えるたびに、次の手順を実行します。「トップ10」リストから10番目のクエリを削除し、更新したばかりのクエリに置き換えて、しきい値も更新します。

「前月」バリアント

同じ「トップ10」リストを保持し、上記と同じ方法で更新します。また、同様のマップを保持しますが、今回は30 * 24 = 720カウント(1時間に1つ)のベクトルを値として保存します。1時間ごとに、すべてのキーに対して次のことを行います。ベクトルから最も古いカウンターを削除し、最後に新しいカウンター(0に初期化)を追加します。ベクトルがすべてゼロの場合は、マップからキーを削除します。また、毎時間「トップ10」リストをゼロから計算する必要があります。

注:はい、今回は1つではなく720の整数を格納しますが、キーの数ははるかに少なくなります(常にバリアントのテールが非常に長い)。

近似

これらの近似は正しいソリューションを保証するものではありませんが、メモリの消費は少なくなります。

  1. 残りをスキップして、N番目ごとのクエリを処理します。
  2. (常時バリアントのみ)マップ内に最大でM個のキーと値のペアを保持します(Mは余裕がある限り大きくする必要があります)。これはLRUキャッシュの一種です。マップにないクエリを読み取るたびに、最も使用頻度の低いクエリをカウント1で削除し、現在処理されているクエリに置き換えます。

近似1の確率論的アプローチが好きです。しかし、近似2(LRUキャッシュ)を使用すると、あまり人気がなかった用語が最初に人気になった場合はどうなりますか?カウントが非常に少ないため、追加されるたびに破棄されませんか?
デル

@delそうです、2番目の近似は特定のクエリストリームに対してのみ機能します。信頼性は低くなりますが、同時に必要なリソースも少なくなります。注:両方の近似を組み合わせることもできます。
ボロ

2

過去1か月の検索キーワードトップ10

以下のような、メモリ効率的な索引付け/データ構造を使用して密集した試行(上のWikipediaのエントリから試行用語の数- )約メモリ要件とnとの間の何らかの関係を定義します。

必要なメモリが利用できる場合(想定1)、正確な月次統計を保持し、毎月それをすべての時間統計に集約できます。

また、「先月」を固定ウィンドウとして解釈するという仮定もあります。しかし、月次ウィンドウがスライドしている場合でも、上記の手順は原則を示しています(スライドは、特定のサイズの固定ウィンドウで近似できます)。

これは、一部の統計が「すべての時間」で計算されることを除いて、ラウンドロビンデータベースを思い出させます(すべてのデータが保持されるわけではありません。rrdは、平均、合計、または最大/最小値を選択することにより、詳細を無視して期間を統合します。特定のタスクで失われる詳細は、エラーを引き起こす可能性のある低頻度アイテムに関する情報です)。

仮定1

1か月間完全な統計を保持できない場合、完全な統計を保持できる特定の期間Pを見つけることができます。たとえば、ある月Pに完全な統計があり、月にn回入るとします。
完璧な統計は機能を定義しますf(search_term) -> search_term_occurance

すべてのn完璧な統計表をメモリに保持できる場合、スライドする月次統計は次のように計算できます。

  • 最新の期間の統計を追加します
  • 最も古い期間の統計を削除します(n完全な統計テーブルを維持する必要があるため)

ただし、集計レベル(月次)でトップ10のみを保持する場合、固定期間の完全な統計から多くのデータを破棄できます。これにより、(期間Pの完全な統計テーブルの上限を仮定して)メモリー要件が固定された作業手順がすでに提供されています。

上記の手順の問題は、スライディングウィンドウの上位10の用語のみに関する情報を保持している場合(すべての場合について同様)、統計は、期間のピークにある検索用語に対しては正しいことになりますが、時間の経過とともに継続的に滴り落ちる検索語の統計。

これは、上位10を超える用語(たとえば、上位100の用語)に関する情報を保持し、上位10が正しいことを期待することで相殺できます。

さらに分析を行うと、エントリが統計の一部になるために必要な最小発生数(最大エラーに関連する)を関連付けることができると思います。

(統計の一部となるエントリを決定する際に、傾向を監視および追跡することもできます。たとえば、各期間の各期間Pの発生の線形外挿により、その用語が1か月または2か月で有意になることがわかった場合はすでに追跡を開始している可能性があります。追跡されたプールから検索語句を削除する場合も同様の原則が適用されます)。

上記の最悪のケースは、ほぼ同じ頻度の用語が多数あり、それらが常に変化する場合です(たとえば、100用語のみを追跡する場合、上位150の用語が同等に頻繁に発生しますが、上位50は最初の月に多く、多くの場合、しばらくしてから統計が正しく保持されないことがあります)。

また、メモリサイズが固定されていない別のアプローチ(厳密には上記のどちらでもない)があり、発生を維持するための発生/期間(日、月、年、常時)に関して最小の重要性を定義します。統計。これにより、集計中の各統計の最大エラーを保証できます(再度ラウンドロビンを参照)。


2

「クロックページ置換アルゴリズム」(「セカンドチャンス」とも呼ばれる)の適応についてはどうですか?検索リクエストが均等に分散されている場合は、非常にうまく機能すると想像できます(つまり、ほとんどの検索された用語は、5mio回続けて表示されるのではなく、定期的に表示され、その後は二度と表示されません)。

これはアルゴリズムの視覚的表現です: クロックページ置換アルゴリズム


0

検索用語の数を巨大なハッシュテーブルに格納します。新しい検索を行うたびに、特定の要素が1つずつ増加します。上位20件程度の検索語を追跡します。11位の要素がインクリメントされたら、#10 *で位置を交換する必要があるかどうかを確認します(上位10位を並べ替える必要はありません。重要なのは、10位と11位の違いを描くことだけです)。

* 新しい検索語句が11位にあるかどうかを確認するには、同様のチェックを行う必要があるため、このアルゴリズムは他の検索語句にもバブルダウンするため、少し単純化しています。


ハッシュテーブルのサイズを制限する必要があります。ユニークな検索のストリームを取得した場合はどうなりますか?定期的ではなく頻繁に検索される用語に気付かれないようにする必要があります。特に、他のすべての検索用語が「現在のイベント」である場合、つまり、今はたくさん検索されているが、来週はそれほど検索されていない場合は、時間の経過とともにそれが一番上の検索用語になる可能性があります。実際、これらのような考慮事項は、あなたが作成したい近似であるかもしれません。正当化すると言ってください。そうするとアルゴリズムの時間がより多くの時間/スペースのコストがかかるため、これらの種類のものをキャッチしません。
cape1232

Googleにはすべてのカウントがあると確信しています -一部のカウントは静的に維持されず、必要に応じて計算されます。
Ether

0

時々、最良の答えは「わからない」です。

深く刺します。私の最初の本能は、結果をQにフィードすることです。プロセスは、Qに入るアイテムを継続的に処理します。プロセスは、

期間->カウント

Qアイテムが処理されるたびに、検索用語を検索してカウントを増やすだけです。

同時に、マップの上位10エントリへの参照のリストを維持します。

現在実装されているエントリについて、その数が上位10の最小のエントリの数よりも大きいかどうかを確認します(リストにない場合)。そうであれば、最小のものをエントリに置き換えます。

私はそれでうまくいくと思います。時間のかかる操作はありません。カウントマップのサイズを管理する方法を見つける必要があります。しかし、それはインタビューの回答には十分なはずです。

彼らはあなたが考えることができるかどうかを確認したい解決策を期待していません。その場で解決策を書く必要はありません...


12
データ構造が呼ばれqueueQ:)文字です。
IVlad

3
面接をしているとしたら、「<stop>わからない」というのは間違いなく最良の答えではありません。即座に決断する。わからない場合は、それを理解するか、少なくとも試してください。
Stephen

インタビューで、7ページに休止状態の人が5回再開するのを見て、彼らがORMとは何なのかわからないときは、すぐにインタビューを終了します。むしろ、彼らはそれを彼らの履歴書に載せないで、単に「私は知らない」と言うだけです。誰もがすべてを知っている。@IVIad、私はC開発者のふりをしてビットを節約しようとしていた...;)
hvgotcodes

0

1つの方法は、すべての検索で、その検索語とそのタイムスタンプを保存することです。このように、任意の期間の上位10を見つけることは、単に、指定された期間内のすべての検索語を比較することです。

アルゴリズムは単純ですが、欠点はメモリと時間が消費されることです。


0

10ノードのスプレイツリーの使用についてはどうですか?ツリーに含まれていない値(検索用語)にアクセスするたびに、葉を捨て、代わりに値を挿入してアクセスします。

この背後にある考え方は、私の他の答えと同じです。検索用語が均等に/定期的にアクセスされるという前提の下で、このソリューションは非常にうまく機能するはずです。

編集する

すぐに再びアクセスされる可能性のあるノードを削除しないようにするために、ツリーにいくつかの検索語句を格納することもできます(同じことが他の回答で提案されているソリューションにも当てはまります)。格納する値が多いほど、結果は良くなります。


0

だめだと思うかどうか。私の解決策はヒープを使用しています。検索項目のトップ10のため、サイズ10のヒープを作成します。次に、このヒープを新しい検索で更新します。新しい検索の頻度がヒープ(最大ヒープ)の上限よりも高い場合は、それを更新します。周波数が最も小さいものを放棄します。

しかし、特定の検索の頻度を計算する方法は、他の何かに頼ります。多分みんなが言ったように、データストリームアルゴリズム...


0

cm-sketchを使用して、最初からのすべての検索の数を保存し、サイズ10の最小ヒープをトップ10に保持します。毎月の結果については、30 cm-sketch / hash-tableとmin-heapを保持し、それぞれを開始します過去30日、29日、1日からのカウントおよび更新。1日が経過したら、最後をクリアして1日目として使用します。毎時の結果と同様に、60のハッシュテーブルと最小ヒープを保持し、最後の60、59、... 1分のカウントを開始します。分のパスとして、最後をクリアし、それを分1として使用します。

毎月の結果は1日の範囲で正確であり、毎時の結果は1分の範囲で正確です


0

固定量のメモリと「無限」(非常に大きなものと考える)のトークンストリームがある場合、この問題は普遍的に解決できません。

大まかな説明...

理由を確認するために、入力ストリーム内のN個のトークンごとに特定のトークン(つまり、ワード)Tを持つトークンストリームを考えます。

また、メモリが最大M個のトークンへの参照(ワードIDとカウント)を保持できると仮定します。

これらの条件により、Nが十分に大きく、ストリーム間にT間で異なるMトークンが含まれる場合、トークンTが検出されない入力ストリームを構築することが可能です。

これは、上位Nアルゴリズムの詳細とは無関係です。限界Mにのみ依存します。

これが真実である理由を確認するために、2つの同一のトークンのグループで構成される受信ストリームを考えてみます。

T a1 a2 a3 ... a-M T b1 b2 b3 ... b-M ...

ここで、aとbはすべてTに等しくない有効なトークンです。

このストリームでは、aiとbiのそれぞれにTが2回表示されていることに注意してください。それでも、システムからフラッシュするのに十分であるように見えることはまれです。

空のメモリから始めて、最初のトークン(T)はメモリ内のスロットを占有します(Mで囲まれています)。次に、a1はスロットを消費し、Mがなくなると、a-(M-1)に到達します。

aMが到着すると、アルゴリズムは1つのシンボルをドロップする必要があるため、それをTにします。次のシンボルはb-1になり、a-1がフラッシュされます。

そのため、Tは実際のカウントを構築するのに十分な時間メモリ常駐しません。要するに、どのアルゴリズムでも、十分なローカル周波数は低いがグローバル周波数は高い(ストリームの長さにわたって)トークンを見逃します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.