説明
私はかなり大きなセット(文字列、文字列、文字列)の一意のタプルを持っています(約40mlnですが、大きくなる可能性があります)。タプルごとに、単一のunsignedint値を計算します。これらの値をどこかに保存して、生成後に再利用できるようにしたいと思います(アプリケーションがダウンした後でも、メモリストレージは問題外であり、データベースも問題外です)。
最初はそれらをタプル(文字列、文字列、文字列、値)としてファイルに保存しましたが、40mlnレコードの読み取りには時間がかかります(ほぼ瞬時に必要になります)。
最初に各(文字列、文字列、文字列)タプルのハッシュ値を計算し、次にそれを[0、n](nは値の数)に正規化し、値のみをソートされた順序でバイナリファイルに格納することにしました。 (正規化されたハッシュ値でソート)。その後、このファイルをmmap()して、mmap [normalize(hash(string、string、string))]で値を取得できます。
私のハッシュ関数は非常に単純ですが高速で、私の場合は機能します(衝突に気づきませんでした):
concatenatedString = s1+"."+s2+"."+s3
unsigned int hash = 31;
for(int i = 0; i < concatenatedString.length(); i++) {
hash = hash * 101 + (unsigned int) concatenatedString[i];
}
正規化と同じ(簡単):
((long) n * hash) / max_value
n-正規化された範囲の上限(約40mln、lowe_bound = 0であるため、nは(n-lower_bound)ではありません)
max_value-古いセットの最大値(私の場合はUINT_MAX、min_value = 0なので、方程式には含めません)
問題
私のハッシュ関数は、0から4,294,967,295(unsigned int)の範囲で均一に分散された値を生成しません(それがどのように実行できるかはわかりません)。このため、正規化後、かなりの数の衝突が発生し、データが失われます(同じ配列インデックスの下で値が上書きされます)。
私がやりたいことを、それらの衝突なしで行うための賢い方法はありますか?
衝突が発生する可能性があることを十分に承知しています。私のアプローチでは、それらはあまりにも頻繁に発生する傾向があります。私のハッシュ範囲は要素の数の100倍なので、これを行う方法があるかもしれないと思いますが、その方法はまだわかりません。
解決策 最終的に、ハッシュをMurmurhashに変更し、正規化メソッドを単純な「モジュロnewRange」に変更し、ファイルの形式を変更しました(すべてのデータを保存します(文字列文字列文字列値))-ファイルはかなり大きくなりましたしかし、そのおかげで、単純な衝突検出メカニズム(ダブルハッシュ)を実装することができました。