盤面(状態) をhash化出来るデータ構造。
起源は、Zobristさんという方がチェスのコンピュータを作成する時に、チェスの盤面をhash化するために作成したhash方法とのこと。
template <class S>
struct Zobrist_hash_set {
public:
Zobrist_hash_set() : v(0) {
mt.seed(rand());
rnd = uniform_int_distribution<ll>(-LLONG_MAX, LLONG_MAX);
}
// 64bit整数値でhashを取る → max(N) = 2^20 = 104856 ≒ 1e6 の時
// hash衝突確率p :
// 1-p <= ( (2^64-n)/2^64 )^n ≒ 0.99999994039
// p ≒ 1e(-7) (宝くじ1等の当選確率位)
// 盤面のhashに関しても同様
// → 調べたい盤面が高々1e6位の時は基本衝突しない。
// 今現在の状態をvとして、xのhashをxorしてあげる
void flip(const S& x) {
// x 初出現 → 新hash値を割当
if (!x_to_hash.count(x)) {
x_to_hash[x] = rnd(mt);
}
// 状態のhash値更新
v ^= x_to_hash[x];
}
// hash初期化
void init() { v = 0; }
// 現時点の状態hash値を返す.
ll get() { return v; }
private:
// hash値
ll v;
// 各 x ∈ X に対するハッシュの割当
unordered_map<S, ll> x_to_hash;
mt19937_64 mt;
uniform_int_distribution<ll> rnd;
};