web-dev-qa-db-ja.com

0x9e3779b9や0x9e3779b1のような「魔法の」ハッシュ定数はどこから来るのですか?

ハッシュテーブルを処理するコードで、定数0x9e3779b9または時々0x9e3779b1を見つけます。例えば

hash = n * 0x9e3779b1 >>> 24

この特定の値が使用されるのはなぜですか?

137
bkgs

0x9e3779b9は、黄金比の小数部の整数部0.61803398875…(sqrt(5)-1)/ 2に2の32乗を掛けたものです。

したがって、φ=(sqrt(5)+1)/ 2 = 1.61803398875が黄金比である場合、ハッシュ関数は、Nice散乱特性を持つn *φの小数部分を計算します。納得させるには、お気に入りのスプレッドシートで(n, n*c-FLOOR(n*c))の散布図を作成し、cをφ、e、πなどに置き換えます。間違っている場合の興味深い実際の問題については- https://lkml.org/lkml/2016/4/29/838

この方法は、「黄金比ハッシュ」または「フィボナッチハッシュ」と呼ばれることが多く、Donald Knuth(The Art of Computer Programming:Volume 3:Sorting and Searching)によって普及しました。理論的には、主にシュタインハウス予想( https://en.wikipedia.org/wiki/Three-gap_theorem )と、倍数の小数部の再帰対称黄金比φ。

時折、0x9e3779b1も表示されます。これは0x9e3779b9に最も近い素数です(これはモジュラーハッシュではないため、「カーゴカルト」のように見えます)。同様に、0x9e3779b97f4a7c15および0x9e3779b97f4a7c55は、これらの数値に相当する64ビットです。

220
32f

他の回答は、マジックナンバーの背後にある意図を説明しています。これは、おそらくあなたが知りたかったものです。しかし、「彼らがどこから来たのか」は、悪いプログラミング慣行からのものであると言えるでしょう。マジックナンバーは悪いので、使用しないでください。上記のような定数には、適切な説明的な変数名を付ける必要があります。また、コメントが定義されている場所に追加する必要があるかもしれません。次に、コード内の値のすべての外観は、名前付き変数の形式である必要があります。あなたがそれらの値を満たしたコードでこれが当てはまる場合、あなたはそもそも彼らの意図に戸惑うことはなかっただろう。

例:

悪い例- マジックナンバー を使用

hash = n * 0x9e3779b1

より良い例-コメントと意味のある変数

# Golden Ratio constant used for better hash scattering
# See https://softwareengineering.stackexchange.com/a/402543 
GOLDEN_RATIO = 0x9e3779b1
hash = n * GOLDEN_RATIO
30
isilanes
ハッシュテーブルを処理するコードで、定数0x9e3779b9または時々0x9e3779b1を見つける

他の答えは、この値が使用される理由を正しく説明しています。ただし、この定数が頻繁に見つかる場合、ハッシュフラッディング攻撃に対して脆弱なコードが見つかることに気付かない場合があります。

ハッシュフラッディング攻撃に対する2つの戦略があります。

  1. 秘密のランダムシードを持つ安全なハッシュ関数を使用します。ハッシュ関数には秘密のランダムシードがありません。 Murmurhash3_32には秘密のランダムシードがありますが、内部状態が小さいため、シードに依存しないマルチコリジョンがあります。ほぼ暗号化されたセキュリティを備え、それでもほぼ許容可能なパフォーマンスを備えた最良のハッシュ関数は、おそらくSipHashです。残念ながら、SHA512などほど遅くはありませんが、遅いです。

  2. 計算が速いハッシュ関数(見つけたハッシュ関数やMurmurhash3_32など)を使用して、各ハッシュバケットをバランスのとれたバイナリ検索ツリーのルートにします。したがって、通常の個別にチェーン化されたハッシュテーブルは、各バケットをリンクリストとして持っています。これは、多くの値が同じバケットにハッシュされる場合は遅くなります。 AVLツリーや赤黒ツリーなどのバランスのとれたバイナリ検索ツリーにすることで、最悪の場合のパフォーマンスが保証されます。

私の意見では、SipHashは非常に遅いため、(2)の方が優れています。また、オペレーティングシステムのカーネルスペースでは、起動段階の初期に秘密のランダムシードを作成するのに十分なエントロピーがない可能性があるため、カーネルスペースでは、起動の初期に乱数を作成する機能がない場合があります。

ハッシュテーブルは広く誤用されています。同じバケットにハッシュする多数の値を送信するだけで、多くのシステムを実質的に停止させるのは簡単です。

5
juhist