大量データインタビューの質問
記事ディレクトリ
1.ハッシュカット
サイズが 100G を超えるログ ファイルがあり、ログに IP アドレスが保存されている場合、最も多く発生する IP アドレスを見つけるアルゴリズムを設計しますか? そして、トップKのIPを見つける方法は? Linux システム コマンドを直接使用する方法は?
- この 100G ファイルを 100 個の小さなファイルに分割できます
- IP アドレスを順番に読み取り、ハッシュ関数を使用して各 IP アドレスを整数に変換し、これらの %100 をこれらの 100 個の小さなファイルに分割します。
- このとき, 同じ IP アドレスをハッシュ関数で変換した整数は同じである必要があり, 最終的に同じファイルに入る必要がある. 異なる ip が同じファイルに入る可能性もある. 今は map コンテナ (map<string, int> countMap ) 小さいファイルごとに順番に回数を数え、A0 を数えた後、A0 の数を最大数 (pair<ip, int> maxIP) として記録し、古いデータをクリアしてから A1 を数え、比較します。 A1 の回数を最大回数で更新し、最大回数を更新し、古いデータを消去して・・・、順番に比較し、回数の多いデータを順番に更新します。
- 上位 K を見つける必要がある場合は、優先度キュー (priority_queue<pair<string,int>, vector<pair<string,int>>, less>) を使用する必要があります。less ファンクターは int のサイズを比較します。ヒープは、最大の K 値を見つけるという条件を満たします。
上記のハッシュ カットの方法を使用すると、最も多く出現する IP アドレスを見つけることができますが、小さな問題があります。特定のファイルが大きすぎるため、同一の IP が多すぎて、この番号付きファイルにマッピングされた IP が多すぎます (おそらくメモリ不足につながります)。ここで、try-catch キャプチャを実行できます。擬似コードは次のとおりです。
try { countMap[ip]++; } catch (exception& e) { //捕获内存不足的异常,说明内存不够 countMap.clear(); //针对这个小文件,再次换个哈希函数,进行哈希切分,再切分成小文件 //再对小文件依次统计 }
topK を検索する Linux コマンドは次のとおりです。
次のファイル IP.log がある場合:
192.168.5.5 234.23.13.44 10.152.16.23 192.168.3.10 192.168.1.4 192.168.2.1 192.168.0.9 10.152.16.23 192.163.0.9 192.168.0.9 69.52.220.44 192.168.1.4 192.168.1.5 192.163.0.9 192.168.2.1 192.168.0.1 192.168.0.2
(1) 行ごとにソートし、結果を標準出力に出力する
sort 文件名
(2) テキストファイルに出現する行数または列数を数えて表示する
uniq -c
(3) 出現回数を逆順に並べ替える
sort -r
(4) 先頭の K 行を表示する
head -k
要約: 発生回数が最も多い上位 K 個の IP を表示します。
sort log_file | uniq -c | sort -nr | head -k
sort log_file 同じ IP アドレスが集まるようにファイルをソートし、uniq -c を使用して重複排除し、各列の横に繰り返し回数を表示します。sort -nr を使用して、この番号で降順にソートします。出現回数が最も多い IP アドレスが前にあり、次に head -k を使用して上位 k 個の IP アドレスを取得します。
操作結果:
2.ビットマップアプリケーション
1. 100 億個の整数が与えられたとき、1 回だけ現れる整数を見つけるアルゴリズムを設計する
100 億個の整数が与えられたとき、一度しか現れない整数を見つけるアルゴリズムを設計してください
タイトルには、一度だけ出現する整数が見つかったことを明確に示しています。これは、整数の出現回数が次の 3 つの状態に分けられることを意味します。
- 0回出現
- 1回出現
- 2回以上
1 ビットは 1 つの状態しか表すことができませんが、2 ビットは 4 種類を表すことができるため、上記の 3 つの状態を表すために 2 ビットを使用します。
- 00(0回)
- 01(1回)
- 10回(2回)
したがって、ここではビットマップを書き換える必要があります。2 つのビットが値を表すため、1 つの char は 4 つの値 (8 ビット / 2) しか表すことができず、100 億の整数で 2^32 * 2 ビットを開く必要があり、これは約 1G を占めます。 .
ここでは 2 ビットで値を表しますが、実際の操作では 2 つのビットセットをカプセル化するクラスを設計し、後続のビット操作を使用して 2 ビットを表して値を表すことができます。
具体的な実装コードは次のとおりです。
template<size_t N> class twobitset { public: void set(size_t x) { if (!_bs1.test(x) && !_bs2.test(x)) // 00 { _bs2.set(x); // 01 } else if (!_bs1.test(x) && _bs2.test(x)) // 01 { _bs1.set(x); _bs2.reset(x); // 10 } // 10 不变 } void PirntOnce() { for (size_t i = 0; i < N; ++i) { if (!_bs1.test(i) && _bs2.test(i)) { cout << i << endl; } } cout << endl; } private: bitset<N> _bs1; bitset<N> _bs2; };
簡単なテスト コードは次のとおりです。
void test_twobitset() { twobitset<100> tbs; int a[] = { 3, 5, 6, 7, 8, 9, 33, 55, 67, 3, 3, 3, 5, 9, 33 }; for (auto e : a) { tbs.set(e); } tbs.PirntOnce(); }
2. 2 つのファイルの共通点を見つける
それぞれ 100 億の整数を持つ 2 つのファイルが与えられた場合、1G のメモリしかありません。2 つのファイルの共通点を見つけるにはどうすればよいでしょうか?
ここでは、わずか 1G のメモリしか占有しない 2 つのビットマップを設計し、次のように設定できます。
- ファイル A をビットマップ 1 に読み取り、ファイル B をビットマップ 2 に読み取ります
- 2 つのビットマップをトラバースし、ビットマップ 1 とビットマップ 2 で AND 演算を実行し、結果をビットマップ 1 に格納します。このとき、ビットマップ 1 にマップされた整数は 2 つのファイルの共通部分です。
3. 100 億個の整数から 2 回以下の整数をすべて見つける
1 つのファイルには 100 億の整数、1G のメモリがあり、2 回以上現れないすべての整数を見つけるアルゴリズムを設計する
この質問の考え方は最初の質問と同じで、出現回数は2ビットで表すことができ、一度は出現回数の状態を表します。
- 0 件 (00)
- 1 件 (01)
- 2回登場(10)
- 3回以上 (11)
ここでも問題を解くために2つのビットマップが必要です.最初の質問のコードを変更するだけです.最終状態が01または10である整数は、出現回数が2を超えない整数です.
template<size_t N> class two_bitset { public: void set(size_t x) { int in1 = _bs1.test(x); int in2 = _bs2.test(x); if (in1 == 0 && in2 == 0)//00 { _bs2.set(x);//01 } else if (in1 == 0 && in2 == 1)//01 { //出现次数置为2 _bs1.set(x);//10 _bs2.reset(x); } else if (in1 == 1 && in2 == 0)//10 { _bs2.set(x);//11 } } private: bitset<N> _bs1; bitset<N> _bs2; };
3.ブルームフィルター
1. 2 つのファイルの交点を見つける (近似アルゴリズム)
2 つのファイルがあり、それぞれに 100 億のクエリがある場合、1G のメモリしかありません。2 つのファイルの共通点を見つける方法は? 近似アルゴリズムを与える
タイトルには近視アルゴリズムが必要です。つまり、いくつかの誤判定が許容されます。その後、ブルーム フィルターを使用できます。
- 最初にファイルの 1 つでクエリを読み取り、それらすべてをブルーム フィルターにマップします。
- 次に、別のファイルのクエリを読み込み、各クエリがブルーム フィルターにあるかどうかを順番に判断し、そうであれば交差 (ただし、誤判断のリスクはありますが、避けられない、これも特徴です)近似アルゴリズムの)、ない 交点であってはなりません。
2. 2 つのファイルの共通点を見つける (正確なアルゴリズム)
2 つのファイルがあり、それぞれに 100 億のクエリがある場合、1G のメモリしかありません。2 つのファイルの共通点を見つける方法は? 正確なアルゴリズムを与える
知らせ:
- ここで明確に指摘されているのは、正確なアルゴリズムが与えられた場合、ブルーム フィルターを使用できないということです。これは、判断ミスが発生するためであり、上記で使用したハッシュ セグメンテーションによって解決する必要があります。
以下のルールに従ってハッシュカットを行います。
- 各クエリは平均 20 バイトで、100 億クエリは 200G であると仮定すると、このようなファイルを 1000 個の小さなファイルに分割できます。
- クエリを 1 つずつ読み込み、各クエリをハッシュ関数で整数に変換し、それぞれ %1000 で 1000 個の小さなファイルに分割します このとき、ファイル A は A0 から A999 までの 1000 個の小さなファイルに分割されます ファイル、ファイルB は B0 ~ B999 に分割され、合計 1000 個の小さなファイルです。
AファイルとBファイルを分割する際に同じハッシュ関数を使用するため、AファイルとBファイルの同じクエリは同じi値を計算し、対応するAiとBiに分割するだけです。 A0 と B0 の交点、A1 と B1 の交点を見つけます... 最後に、これらのセットを組み合わせると、ファイル A とファイル B の総交点になります。
小さなファイルの交差点を見つける方法も、次のように非常に簡単です。
- 小さなファイルの 1 つをメモリにロードしてセット コンテナーに配置し、別の小さなファイルでクエリをトラバースし、各クエリがセット コンテナー内にあるかどうかを順番に判断できます。そうでない場合は、交差点ではありません。 .
- 小さなファイルが大きすぎる場合は、ファイルを再度分割して、プロセスを再度実行する必要があります。
3. カウンティング方式により、Bloom フィルターが削除をサポート
要素の削除操作をサポートするように BloomFilter を拡張する方法は?
1 つの要素が削除されると、他の要素が影響を受ける可能性があるため、ブルーム フィルターは削除を直接サポートできません。削除をサポートする 1 つのメソッド (count メソッドの削除):
- ブルーム フィルターの各ビットを小さなカウンターに展開し、要素を挿入するときに k カウンター (k ハッシュ関数によって計算されるハッシュ アドレス) に 1 を追加し、要素を削除するときに k カウンターをデクリメントします。空。
欠陥:
- 要素が実際にブルーム フィルターにあるかどうかを確認できません
- プレゼンス カウント ラップ
要約:
- ブルームフィルタは直接削除をサポートしていません. 最終的な分析では, 主にスペースを節約して効率を向上させるために使用されます. カウントして削除する場合, ファイルまたはディスクを走査して削除する要素が存在することを確認する必要があります.ファイル IO とディスク IO の速度は比較的高速ですが、非常に遅く、ビットマップの各ビットに追加のカウンターを設定するには、元のビットマップの数倍のストレージ スペースが必要であり、これは小さなコストではありません。削除をサポートしている場合、スペースをあまり節約できず、Bloom フィルターの必須要件に違反しています。