次のような面接の質問を受けました:
40 億個の整数を含む入力ファイルが与えられた場合、ファイルに含まれていない整数を生成するアルゴリズムを提供します。メモリが 1 GB であると仮定します。メモリが 10 MB しかない場合に何を行うかを考えてみましょう。
私の分析:
ファイルのサイズは 4×10 9 ×4 バイト = 16 GB です。
外部ソートを実行することで、整数の範囲を知ることができます。
私の質問は、ソートされた大きな整数セットで欠落している整数を検出する最良の方法は何かということです。
私の理解(すべての回答を読んだ後):
32 ビットの整数について話していると仮定すると、2 32 = 4*10 9 個の異なる整数が存在します。
ケース 1: 1 GB = 1 * 10 9 * 8 ビット = 80 億ビットのメモリがあります。
解決:
1 つの異なる整数を表す 1 ビットを使用すれば十分です。ソートは必要ありません。
実装:
int radix = 8;
byte[] bitfield = new byte[0xffffffff/radix];
void F() throws FileNotFoundException{
Scanner in = new Scanner(new FileReader("a.txt"));
while(in.hasNextInt()){
int n = in.nextInt();
bitfield[n/radix] |= (1 << (n%radix));
}
for(int i = 0; i< bitfield.lenght; i++){
for(int j =0; j<radix; j++){
if( (bitfield[i] & (1<<j)) == 0) System.out.print(i*radix+j);
}
}
}
ケース2: 10 MBのメモリ = 10 * 10 6 * 8 ビット = 8000万ビット
解決:
すべての可能な 16 ビット プレフィックスには、2 16個の整数 = 65536個あり、2 16 * 4 * 8 = 200 万ビットが必要です。65536 個のバケットを構築する必要があります。最悪の場合、40 億個の整数すべてが同じバケットに属するため、各バケットにはすべての可能性を保持する 4 バイトが必要です。
- ファイルの最初のパスを通じて各バケットのカウンターを構築します。
- バケットをスキャンし、ヒット数が 65536 未満の最初のバケットを見つけます。
- ファイルの2回目のパスでステップ2で見つかった上位16ビットプレフィックスを持つ新しいバケットを作成します。
- ステップ 3 で構築されたバケットをスキャンし、ヒットがない最初のバケットを見つけます。
コードは上記のものと非常に似ています。
結論: ファイルパスを増やすことでメモリを削減します。
遅れて来た人のために説明しておきます。質問では、ファイルに含まれていない整数が 1 つだけあるとは言っていません。少なくとも、ほとんどの人はそうは解釈しません。ただし、コメント スレッドのコメントの多くは、そのタスクのバリエーションに関するものです。残念ながら、コメント スレッドにそのタスクを紹介したコメントは、後に作成者によって削除されたため、孤立した返信はすべて誤解しているように見えます。非常に混乱しています。申し訳ありません。
ベストアンサー1
「整数」が 32 ビットを意味すると仮定すると、10 MB のスペースは、入力ファイル内の任意の 16 ビット プレフィックスを持つ数字がいくつあるかを、入力ファイルを 1 回通過してすべての可能な 16 ビット プレフィックスについて数えるのに十分すぎるほどです。少なくとも 1 つのバケットは、2 16回未満しかヒットしません。2 回目の通過を実行して、そのバケット内の可能な数字のうちどれがすでに使用されているかを調べます。
32 ビットを超えるが、サイズが制限されている場合: 上記のように実行し、(符号付きまたは符号なし、選択可能) 32 ビットの範囲外になるすべての入力数値を無視します。
「整数」が数学的な整数を意味する場合: 入力を 1 回読み取り、 これまでに見た中で最も長い数値の
最大の
長さを記録します。完了したら、
最大値に 1 を加えた、
桁が 1 つ多いランダムな数値を出力します。(ファイル内の数値の 1 つは、正確に表現するには 10 MB 以上かかるビッグナンバーである可能性がありますが、入力がファイルである場合は、少なくともそのファイルに収まるものの長さを表すことができます)。