H(A)
とH(B)
の2つのハッシュがあり、それらを結合するとします。 2つのハッシュを結合する良い方法は、それらをXOR
することだと読んでいます。 XOR( H(A), H(B) )
。
私が見つけた最良の説明は、これらの ハッシュ関数ガイドライン についてここで簡単に触れられます:
ほぼランダムな分布で2つの数値のXORをとると、まだほぼランダムな分布*の別の数値が得られますが、現在は2つの値に依存しています。
...
*結合する2つの数値の各ビットで、2つのビットが等しい場合は0、そうでない場合は1が出力されます。つまり、組み合わせの50%で1が出力されます。そのため、2つの入力ビットがそれぞれ0または1になる可能性がおよそ50-50である場合、出力ビットも同様です。
XORがハッシュ関数を結合するためのデフォルトの操作である必要がある理由(ORまたはANDなどではなく)の背後にある直感や数学を説明できますか?
一様にランダムな(1ビット)入力を仮定すると、AND関数の出力確率分布は75%0
および25%1
。逆に、ORは25%0
および75%1
。
XOR関数は50%0
および50%1
したがって、均一な確率分布を組み合わせるのに適しています。
これは、真理値表を書くことで確認できます。
a | b | a AND b
---+---+--------
0 | 0 | 0
0 | 1 | 0
1 | 0 | 0
1 | 1 | 1
a | b | a OR b
---+---+--------
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 1
a | b | a XOR b
---+---+--------
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 0
演習:2つの1ビット入力a
およびb
の論理関数には、この均一な出力分布がいくつありますか?なぜXORがあなたの質問に記載されている目的に最も適しているのですか?
便利なビット混合特性にもかかわらず、XORはnotであり、その可換性によりハッシュを結合するのに適した方法です。 10タプルのハッシュテーブルの{1、2、…、10}。
より良い選択はm * H(A) + H(B)
です。ここで、mは大きな奇数です。
クレジット:上記のコンバイナーはBob Jenkinsからのヒントです。
Xorはハッシュを結合するための「デフォルト」の方法かもしれませんが、グレッグヒューギルの答えは、落とし穴がある理由も示しています。2つの同一のハッシュ値のxorはゼロです。実生活では、同一のハッシュが予想されるよりも一般的です。その後、これらの(それほど頻繁ではない)コーナーケースでは、結果の結合ハッシュが常に同じ(ゼロ)であることがわかります。ハッシュの衝突は、予想よりもはるかに頻繁に発生します。
考案された例では、管理するさまざまなWebサイトのユーザーのハッシュ化されたパスワードを組み合わせることができます。残念ながら、多数のユーザーがパスワードを再利用しているため、生成されるハッシュの驚くべき割合はゼロです!
このページを見つけた他の人のために、私が明示的に指摘したいことがあります。 ANDおよびOR BlueRajaのような出力を制限する-Danny Pflughoeは指摘しようとしていますが、より適切に定義できます。
まず、これを説明するために使用する2つの単純な関数、Min()とMax()を定義します。
Min(A、B)は、AとBの間の小さい値を返します。たとえば、Min(1、5)は1を返します。
Max(A、B)は、AとBの間の大きい値を返します。たとえば、Max(1、5)は5を返します。
指定されている場合:_C = A AND B
_
その後、C <= Min(A, B)
を見つけることができます。これは、AまたはBの0ビットと1にするためにANDできるものがないためです。したがって、すべてのゼロビットはゼロビットのままであり、すべての1ビットがゼロビットになる可能性があります(したがって、より小さい値になります)。
あり:_C = A OR B
_
反対は真です。C >= Max(A, B)
これで、AND関数の結果がわかります。すでに1であるビットはORでゼロになることはできないため、1のままになりますが、すべてのゼロビットは1になる可能性があるため、より大きな数になります。
これは、入力の状態が出力に制限を適用することを意味します。 90でANDを行うと、他の値が何であるかに関係なく、出力が90以下になることがわかります。
XORの場合、入力に基づく暗黙の制限はありません。 XOR 255のバイトよりも逆のバイトを取得する場合でも、そのバイトから可能なバイトを出力できることがわかる特別なケースがあります。すべてのビットは、他のオペランドの同じビット。
XOR
にバイアスをかけた入力でランダムな入力をすると、出力はランダムになります。 AND
またはOR
についても同様ではありません。例:
00101001 XOR 00000000 = 00101001 00101001 AND 00000000 = 00000000 00101001 OR 11111111 = 11111111
@Greg Hewgillが述べているように、both入力がランダムであっても、AND
またはOR
を使用すると、出力にバイアスがかかります。
より複雑なものよりもXOR
を使用する理由は、必要がないということです。XOR
は完全に機能し、非常に高速です。
左側の2列を覆い、入力のみが出力のみを使用しているものを見つけようとします。
a | b | a AND b
---+---+--------
0 | 0 | 0
0 | 1 | 0
1 | 0 | 0
1 | 1 | 1
1ビットを見たとき、両方の入力が1であることがわかりました。
XORについても同じことを行います
a | b | a XOR b
---+---+--------
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 0
XORは入力について何も提供しません。
[〜#〜] xor [〜#〜]は、[〜#〜] or [〜#〜]および[〜#〜] and [〜#〜]。
AND(X、Y)を例に取り、入力[〜#〜] x [〜#〜]にfalseを入力すると、入力[〜#〜] y [〜#〜]は重要ではありません...そして、ハッシュを結合するときに入力を重要にしたいと思うでしょう。
XOR(X、Y)とすると、[〜#〜] both [〜#〜]入力[〜#〜] always [〜#〜]マター。 Yが重要でない場合、Xの値はありません。 XまたはYのいずれかが変更された場合、出力にはそれが反映されます。
Java.util.Arrays のhashCode()
のさまざまなバージョンのソースコードは、堅牢で一般的な使用のハッシュアルゴリズムの優れたリファレンスです。それらは簡単に理解され、他のプログラミング言語に翻訳されます。
大まかに言うと、ほとんどのマルチ属性hashCode()
実装は次のパターンに従います。
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
他のStackOverflowのQ&Aを検索して、31
、およびなぜJavaコードは非常に頻繁にそれを使用します。不完全ですが、非常に優れた一般的なパフォーマンス特性を持っています。