回答:
インタビューであろうと実際の仕事であろうと、あなたにとって一番の優先事項はあなたにとって意味のある実用的なソリューションである必要があります。これは通常、あなたは、シンプルで使いやすいためであることを考えることができ、第一の溶液を提供する必要があることを意味しますが、説明することを。
私にとって、それは数字を並べ替え、ギャップをスキャンすることを意味します。しかし、私はビジネスシステムとWebアプリに取り組んでいます。私は少しもいじりませんし、私のチームにもしたくありません!
低レベルの、より金属に近い仕事のためにインタビューしている場合、「ソート」はおそらく空白の視線で満たされます。彼らはあなたにあなたがビットなどについて快適に考えることを望んでいます。最初の答えは、「ああ、ビットマップを使用します」です。(またはビット配列、またはビットセット。)
そして、どちらにしても、「間違った」解決策を与えたとしても、インタビュアー(または上司!)がそれを求めた場合、マネージャーの特定の関心領域に焦点を合わせて、いくつかの改善または代替案を提案できます。
O(n*log(n))
ます。(または整数バケットソートの場合はO(n)!)BitSet
/ BitMap
/ BitArray
)BitArray
を使用して「見つかった数字」にフラグを立てます。次に、をスキャンし0
ます。BitArray
/に対する別のパスですBitSet
(0
'sを見つけるため)。それだO(n)
、私は思う!または何でも。
実際に抱えている懸念に対処します。必要に応じて素朴なソリューションを使用して、最初に問題を解決してください。まだ存在しない懸念に対処するために全員の時間を無駄にしないでください。
ファイルであるため、複数のパスを作成できると想定しています。最初に256個のカウンターの配列を作成し、ファイルを反復処理し、数値ごとに、数値の最初のバイトとしてインデックス付けされたカウンターをインクリメントします。完了したら、ほとんどのカウンターは2 ^ 24になっているはずですが、1〜4個のカウンターの値はもっと小さいはずです。これらの各インデックスは、欠落している数値の1つの最初のバイトを表します(4未満の場合は、複数の欠落した数値が同じ最初のバイトを共有するためです)。
これらのインデックスごとに、256カウンターの別の配列を作成し、ファイルに2回目のパスを作成します。今回は、最初のバイトが以前の値の1つである場合、2番目のバイトに基づいて配列のカウンターをインクリメントします。完了したら、2 ^ 16未満のカウンタを再度検索すると、不足している数字の2番目のバイトがあり、それぞれが最初のバイトに一致します。
3番目のバイト(各バイトの後に最大4つの異なるバイトが続く場合でも、各パスで最大4つの配列が必要であることに注意してください)および4番目のバイトで、すべての不足している数字を見つけました。
時間の複雑さ- O(n * log n)
スペースの複雑さ- 一定!
実際にはn=2^32
、これがパラメーターであると考えましたが、欠落している数字の数k=4
もパラメーターです。k<<n
これがスペースの複雑さを意味すると仮定しO(k)
ます。
ちょうど楽しみのために(と私は現在、錆を学ぶためにしようとしているので)私は錆でそれを実装:https://gist.github.com/idanarye/90a925ebb2ea57de18f03f570f70ea1f。オンワンは〜2 ^ 32の数値で実行するため、テキスト表現を選択しました...
これがJavaであれば、BitSetを使用できます。そのうちの2つは、32ビットの数値をすべて保持できないためです。骨格コード、おそらくバグあり:
BitSet bitsetForPositives = new Bitset(2^31); // obviously not 2^31 but you get the idea
BitSet bitsetForNegatives = new Bitset(2^31);
for (int value: valuesTheyPassInSomehow) {
if ((value & 0x80000000) == 0)
bitsetForPositives.set(value );
else
bitsetForNegatives.set(value & ~0x80000000);
}
次に、BitSet.nextClearBit()
行方不明者の検索に使用 します。
後で追加されたメモ:
このアルゴリズムを使用すると、時間のかかる部分を並行して実行するのはかなり簡単です。元のファイルが4つのほぼ等しい部分に分割されているとします。4組のBitSetを割り当てます(2GB、依然として管理可能)。
I / Oがレート制限のステップであると期待していますが、魔法のようにすべての数値がメモリ内にあれば、本当に速度を上げることができます。
Integer.MIN_VALUE
正しく処理しません。それを修正するために否定する代わりに、符号ビットをマスクすることができます。
bool GetBit(byte[] byteArray, uint index) { var byteIndex = index >> 3; var bitInByte = index & 7; return (byteArray[byteIndex] >> bitInByte) & 1 != 0; }
この問題は、ビットの配列(true / false)を使用して解決できます。これは、特定の番号が見つかったかどうかを保持するために配列のインデックスを使用して、すべての番号の答えを保持するための最も効率的な構造でなければなりません。
C#
var bArray = new BitArray(Int32.MaxValue);
//Assume the file has 1 number per line
using (StreamReader sr = File.OpenText(fileName))
{
string s = String.Empty;
while ((s = sr.ReadLine()) != null)
{
var n = int32.Parse(s);
bArray[n] = true;
}
}
次に、配列を繰り返し処理しますが、まだfalseである値については、ファイルにはありません。
ファイルを小さなチャンクに分割することもできますが、Windows 7(64ビット)を実行している16.0 GBのラップトップに完全なint32最大サイズの配列(2147483647)を割り当てることができました。
64ビットを実行していなくても、より小さいビット配列を割り当てることができました。ファイルを前処理して、使用可能な環境リソースに適した[0-64000] [64001-128000]などの番号の範囲を持つ番号の小さいファイルのセットを作成します。大きなファイルを調べて、それぞれの番号を対応するセットファイルに書き込みます。次に、小さなファイルをそれぞれ処理します。前処理ステップのために少し時間がかかりますが、リソースが限られている場合はリソースの制限を回避できます。
これは面接の質問なので、面接官に制約についての理解を示します。それでは、「すべての可能な数」とはどういう意味ですか?皆が推測するように、本当に0 ... 2 <(32-1)ですか?通常の32ビットアーキテクチャは、32ビット以上の数を扱うことができます。明らかに、単なる表現の問題です。
32ビットシステムで解決する必要がありますか、それとも数字の制限の一部ですか?たとえば、一般的な32ビットシステムでは、ファイルを一度にRAMにロードすることはできません。また、32ビットシステムでは、ファイルサイズの制限により、すべての数字を含むファイルを作成できないことがよくあります。まあ、「これらの4つを除くすべての数字」のような巧妙なエンコーディングがなければ、問題は簡単に解決されます。
しかし、「0から2 ^(32-1)までのすべての数字を含むファイルを指定して、不足しているものを教えてください」という質問を本当に理解したい場合は(これは大きな場合です!)、それを解決する方法はたくさんあります。
些細だが実行不可能:各可能な番号について、ファイルをスキャンし、そこにあるかどうかを確認します。
512 MBのRAMとシングルパスファイル:ファイルから読み取られたすべての番号をマーク(=そのインデックスにビットを設定)し、その後、RAMを1回通過して不足しているものを確認します。
覚えやすく、面接で明言しやすいアプローチの1つは、Nビットのすべての数値を見ると、各ビットがそれらの値のちょうど半分に設定され、他の半分には設定されないという事実を使用することです。
ファイル内のすべての値を反復処理し、最後に32個の値を保持すると、正確に(2 ^ 32/2)またはその値よりわずかに小さい32個の値になります。最大(2 ^ 32/2)と合計の差により、欠損値の各位置に設定された合計ビットが得られます。
それが得られたら、それらの合計を与える可能性のある4つの値のすべての可能なセットを決定できます。その場合、ファイルの値を再度調べて、それらの組み合わせの一部である値を確認できます。見つかった場合、その値を含む組み合わせは可能性として排除されます。可能な組み合わせが1つだけ残ったら、答えてもらいます。
たとえば、ニブルを使用すると、次の値があります。
1010
0110
1111
0111
1101
1001
0100
0101
0001
1011
1100
1110
各位置に設定される合計ビットは次のとおりです。
7867
それらを8(4 ^ 2/2)から減算すると、次のようになります。
1021
つまり、次の4つの値の可能なセットがあるということです。
1000
0000
0011
0010
1010
0001
0010
0000
(見逃した場合はご容赦ください、私はこれを一目で見ているだけです)
元の数字をもう一度見ると、1010がすぐに見つかります。これは、最初のセットが答えだったことを意味します。
determine all the possible sets of 4 values that could give those totals
ます。これはあなたの答えに欠けている解決策の重要な部分だと本当に思います。また、時間とスペースの複雑さに影響を与える可能性があります。
ファイルが増加する番号でソートされていると仮定します。
確かに(2³²-4)の数字が含まれていることを確認してください。
ファイルが完全な場合(または、4つの欠落した数字が最後の4つの数字であった場合)、ファイルの位置Nで単語を読み取ると、一致する値Nが返されます。
位置[0..2³²-4-1)で二分法検索を使用して、最初の予期しない番号X1を検索します。
最初の不足している番号が見つかったら、位置[X1 ..(2³²-4-1)]で再度二重検索を実行して、2番目の不足しているX2を見つけます。不足している番号がなくなった場合(1つの不足している番号を渡したため)。
残りの2つの数値についても同様に繰り返します。3回目の反復では、位置Nの単語を読み取るとN-2が返され、4回目にはN-3が返されます。
警告:これはテストしていません。しかし、私はそれがうまくいくと思う。:)
現実には、他の答えにも同意します。最初の質問は環境に関するものです。RAMがありますか(どれだけ)、ファイルはダイレクトアクセスストレージデバイスにありますか、これはワンショット操作(最適化は不要)または重要な操作(各サイクルカウント)ですか、外部ソートユーティリティがありますかなど。
その後、コンテキストに合った妥協案を見つけます。これは少なくとも、アルゴリズムを探す前に問題の分析を開始することを示しています。
すべての標準的な質問と同様、解決策はインタビューの前にグーグルでグーグルすることです。
この質問とバリエーションには、すべての数値のXOR演算を含む非常に明確な「正しい」答えがあります。データベースまたは何かのインデックスを理解することを示すことになっています。したがって、「機能する可能性はあるが、論文で述べられていることではない」という答えはゼロです。
プラス面には、これらの質問の有限セットがあり、数時間の修正により、天才のように見えます。頭の中でそれを解決しているふりをすることを忘れないでください。
編集。ああ、4ではXORとは異なるアプローチがあるようです
編集。Downvoters:これは、OPに記載されている正確な問題に対する公開された教科書O(n)ソリューションです。