バックグラウンド:
重複が確実に含まれる正の乱数のN長配列があります。例:10,4,5,7,10,9,10,9,8,10,5
編集:Nは32、またはそのサイズの2のその他の累乗である可能性があります。
問題:
重複を0から(N-1)までの欠落している番号に置き換える最速の方法を見つけようとしています。上記の例を使用して、次のような結果が必要です
。10,4,5,7,0,9,1,2,8,3,6
目標は、0からN-1までの各数値の1つを持つことです。 、すべての数字を0-(N-1)に置き換えるだけではありません(ランダムな順序が重要です)。
編集:この置換が決定論的であることも重要です。つまり、同じ入力が同じ出力(ランダムではない)を持ちます。
私の解決策:
現在Javaで実装されており、2つのブール配列を使用して使用済み/未使用の数値([0、N)の範囲の一意の数値/欠落している数値)を追跡し、おおよその最悪の場合の実行時間はN + N * sqrt(N)です。 。
コードは次のとおりです。
public byte[] uniqueify(byte[] input)
{
boolean[] usedNumbers = new boolean[N];
boolean[] unusedIndices = new boolean[N];
byte[] result = new byte[N];
for(int i = 0; i < N; i++) // first pass through
{
int newIdx = (input[i] + 128) % N; // first make positive
if(!usedNumbers[newIdx]) // if this number has not been used
{
usedNumbers[newIdx] = true; // mark as used
result[i] = newIdx; // save it in the result
}
else // if the number is used
{
unusedIndices[i] = true; // add it to the list of duplicates
}
}
// handle all the duplicates
for(int idx = 0; idx < N; idx++) // iterate through all numbers
{
if(unusedIndices[idx]) // if unused
for(int i = 0; i < N; i++) // go through all numbers again
{
if(!usedNumbers[i]) // if this number is still unused
{
usedNumbers[i] = true; // mark as used
result[i] = idx;
break;
}
}
}
return result;
}
これは私が望むことができる最速のように思えますが、私よりもはるかに賢い人々がより良い解決策を持っているかもしれないので、私はインターネットに尋ねると思いました。
注意:提案/解決策はJavaである必要はありません。
ありがとうございました。
編集:私はこれをC++に変換していることを言及するのを忘れました。より完全なので、Java実装を投稿しました。