コーディングするハッシュテーブル用に、C++でパフォーマンス指向のハッシュ関数を実装する必要があります。私はすでに周りを見回して、「一般的に」良いハッシュ関数とは何かを尋ねる質問だけを見つけました。私は、CRC32(ただし、適切な実装を見つける場所は?)といくつかの暗号化アルゴリズムを検討しました。しかし、私のテーブルには非常に特定の要件があります。
テーブルは次のようになります。
100,000 items max
200,000 capacity (so the load is 0.5)
hashing a 6-character string which is a part of English sentence
examples: "become" "and he" ", not "
私のハッシュテーブルの最優先事項はクイック検索(取得)です。クイック挿入は重要ではありませんが、クイック検索と一緒に使用されます。削除は重要ではなく、再ハッシュは調査対象ではありません。衝突を処理するために、おそらく ここ で説明されているように、個別のチェーンを使用します。私はすでに この記事 を見てきましたが、以前にそのようなタスクを処理したことがある人の意見をお願いします。
今あなたがハッシュを望んでいて、何かが欲しいと仮定すると超高速これはあなたのケースで機能します、あなたの文字列は6文字の長さしかないので、この魔法を使うことができます:
size_t precision = 2; //change the precision with this
size_t hash(const char* str)
{
return (*(size_t*)str)>> precision;
}
CRCはスローポーク用です;)
説明:これは、文字列ポインタの内容をsize_t(ハードウェアに最適な一致に基づいてint32またはint64)に「見える」ようにキャストすることで機能します。したがって、文字列の内容は生の数値として解釈され、文字の心配はなくなり、必要な精度でビットシフトします(この数値を最高のパフォーマンスに調整します。2は文字列のハッシュに適しています。数千のセット)。
また、本当にすてきな部分は、最新のハードウェアの適切なコンパイラが1つのアセンブリ命令でこのような文字列をハッシュすることです。
この単純な多項式は驚くほどうまく機能します。私は、さまざまなハッシュ関数とハッシュ乗数を研究したMicrosoft ResearchのPaul Larsonからそれを得ました。
unsigned hash(const char* s, unsigned salt)
{
unsigned h = salt;
while (*s)
h = h * 101 + (unsigned) *s++;
return h;
}
salt
は、ハッシュテーブルが ハッシュテーブル攻撃を防御するために作成される前に、ランダムに選択された値に初期化する必要があります 。これが問題ではない場合は、0を使用してください。
衝突を最小限に抑えるには、テーブルのサイズも重要です。あなたのような音で結構です。
Boost.Functional/Hash が役立つかもしれません。私は試したことがないので、その性能を保証することはできません。
Boostには CRCライブラリ もあります。
私は Boost.Unordered を最初に見ます(つまり、boost :: unordered_map <>)。コンテナーのバイナリツリーの代わりにハッシュマップを使用します。
一部のSTL実装では、stdext名前空間にhash_map <>コンテナがあると思います。
テーブルのサイズによって、使用するハッシュのサイズが決まります。もちろん衝突を最小限にしたいと思います。最大アイテムと容量で何を指定しているのかわかりません(どちらも同じように見えます)。いずれの場合も、これらの数値のいずれかで32ビットハッシュで十分であることを示唆しています。 CRC16(約65,000の可能性)で済むかもしれませんが、処理する衝突がたくさんあるでしょう。一方、衝突はCRC32ハッシュよりも処理が速い場合があります。
CRC32を使用します。ドキュメンテーションとサンプルコードが不足することはありません。最大値を把握し、速度を優先するため、ポインタの配列を使用します。ハッシュを使用してインデックスを生成します。衝突時に、空のバケットに到達するまでインデックスをインクリメントします。すばやく簡単です。
英語の単語を保存するので、ほとんどの文字は文字であり、データの最上位2ビットに大きな変動はありません。その上、XORを使用するだけで非常にシンプルに保つことができます。結局のところ、暗号の強度を求めているのではなく、合理的に均等な分布を求めているだけです。これらの線に沿った何か:
size_t hash(const std::string &data) {
size_t h(0);
for (int i=0; i<data.length(); i++)
h = (h << 6) ^ (h >> 26) ^ data[i];
}
return h;
}
それ以外に、ハッシュ関数としてのstd :: tr1 :: hashやハッシュテーブルの実装としてのstd :: tr1 :: unordered_mapを見たことはありますか?これらを使用すると、独自のクラスを実装するのとは対照的に、多くの作業を節約できます。
短い文字列を検索する必要があり、挿入が問題にならない場合は、Bツリーまたは2-3ツリーを使用することもできますが、この場合、ハッシュしてもあまり効果がありません。
これを行う方法は、各ノードに文字を配置することです。最初にノード "a"をチェックし、次に "a"の子の "p"をチェックし、次にその子の "p"をチェックし、次に " l」、次に「e」。 「Apple」と「apply」がある状況では、最後のノードにシークする必要があります(唯一の違いは最後の「e」と「y」にあるため)
しかし、ほとんどの場合、ほんの数ステップ( "xylophone" => "x"-> "ylophone")でWordを取得できるため、このように最適化できます。これはハッシュよりも高速です
私のハッシュテーブルの最優先事項は、クイック検索(取得)です。
ハッシュテーブルでの検索はO(1)なので、適切なデータ構造を使用しています。 :)
CRC32は問題なく動作するはずです。実装はそれほど複雑ではなく、主にXORに基づいています。良い多項式を使用していることを確認してください。
単純なものはどうですか:
// Initialize hash lookup so that it maps the characters
// in your string to integers between 0 and 31
int hashLookup[256];
// Hash function for six character strings.
int hash(const char *str)
{
int ret = 0, mult = 1;
for (const char *p = str; *p; *p++, mult *= 32) {
assert(*p >= 0 && *p < 256);
ret += mult * hashLookup[*p];
}
return ret;
}
これは32ビット整数を想定しています。文字あたり5ビットを使用するため、ハッシュ値には30ビットしかありません。おそらく、最初の1文字または2文字に対して6ビットを生成することで、これを修正できます。文字セットが十分に小さい場合は、30ビットを超える必要はない可能性があります。
C++ 11以降、C++は std::hash< string >( string )
を提供しています。これは、ほとんどの文字列に ハッシュコードの適切な分布 を提供する効率的なハッシュ関数である可能性があります。
さらに、ハッシュテーブルの実装を考えている場合は、C++の使用を検討する必要があります std::unordered_map
代わりに。