VS2005 の標準ハッシュ関数は、高パフォーマンスのルックアップを実現しようとすると非常に遅いことがわかりました。ほとんどの衝突を無効にする高速で効率的なハッシュ アルゴリズムの良い例は何ですか?
11 に答える
私は Microsoft Research のPaul Larsonと協力して、いくつかのハッシュテーブルの実装を行いました。彼はさまざまなデータセットで多数の文字列ハッシュ関数を調査し、単純な 101 の乗算と加算のループが驚くほどうまく機能することを発見しました。
unsigned int
hash(
const char* s,
unsigned int seed = 0)
{
unsigned int hash = seed;
while (*s)
{
hash = hash * 101 + *s++;
}
return hash;
}
私の古いコードから:
/* magic numbers from http://www.isthe.com/chongo/tech/comp/fnv/ */
static const size_t InitialFNV = 2166136261U;
static const size_t FNVMultiple = 16777619;
/* Fowler / Noll / Vo (FNV) Hash */
size_t myhash(const string &s)
{
size_t hash = InitialFNV;
for(size_t i = 0; i < s.length(); i++)
{
hash = hash ^ (s[i]); /* xor the low 8 bits */
hash = hash * FNVMultiple; /* multiply by the magic number */
}
return hash;
}
これは速い。本当にめちゃくちゃ速い。
Boost には、ほとんどの一般的な型に対していくつかの基本的なハッシュ関数を提供できるboost::hashライブラリがあります。
それは常にデータセットに依存します。
ひもにCRC32を使うことで、驚くほど良い結果が得られました。さまざまな入力セットで非常にうまく機能します。
多くの優れた CRC32 実装は、ネット上で簡単に見つけることができます。
編集:ほとんど忘れていました:このページには、パフォーマンス数値とテストデータを備えた素敵なハッシュ関数の銃撃戦があります:
http://smallcode.weblogs.us/ <-- ページのさらに下。
私は Jenkins ハッシュを使用してブルーム フィルター ライブラリを作成しました。これは優れたパフォーマンスを発揮します。
詳細とコードはこちらから入手できます: http://burtleburtle.net/bob/c/lookup3.c
これは、Perl がハッシュ操作 fwiw に使用するものです。
Python 3.4 には、 SipHashに基づく新しいハッシュ アルゴリズムが含まれています。PEP 456は非常に有益です。
私は少し検索を行いましたが、面白いことに、ポール・ラーソンの小さなアルゴリズムがここに表示されました http://www.strchr.com/hash_functions は、多くの条件でテストされた中で最も衝突が少なく、非常に高速です。アンロールまたはテーブル駆動。
ラーソンは単純に 101 を掛けて上記のループを加算します。
文字列ハッシュの古典的な提案の 1 つは、文字を 1 つずつステップ実行して、ASCII/Unicode 値をアキュムレータに追加し、そのたびにアキュムレータに素数を掛けることです。(ハッシュ値のオーバーフローを許可)
template <> struct myhash{};
template <> struct myhash<string>
{
size_t operator()(string &to_hash) const
{
const char * in = to_hash.c_str();
size_t out=0;
while(NULL != *in)
{
out*= 53; //just a prime number
out+= *in;
++in;
}
return out;
}
};
hash_map<string, int, myhash<string> > my_hash_map;
データを捨てずにそれより速くするのは難しいです。文字列の内容全体ではなく、数文字だけで文字列を区別できることがわかっている場合は、より迅速に行うことができます。
値が頻繁に計算される場合は、ハッシュ値を記憶する basic_string の新しいサブクラスを作成して、ハッシュ値をより適切にキャッシュしてみてください。ただし、 hash_map は内部でそれを行う必要があります。
文字列が平均して1つのキャッシュ行よりも長いが、長さ+プレフィックスがかなり一意である場合は、長さ+最初の8/16文字だけにすることを検討してください。(長さはstd :: stringオブジェクト自体に含まれているため、読みやすくなっています)