Scala: MurmurHash を実装する
Redis の MurmurHash2 実装をそのまま Scala で書いてみる。
コード
効率よりも分かりやすさを優先。
object MurmurHash { private val defaultSeed = 0xadc83b19L def murmurHash64A(data: Seq[Byte], seed: Long = defaultSeed): Long = { val m = 0xc6a4a7935bd1e995L val r = 47 val f: Long => Long = m.* val g: Long => Long = x => x ^ (x >>> r) val h = data.grouped(8).foldLeft(seed ^ f(data.length)) { case (y, xs) => val k = xs.foldRight(0L)((b, x) => (x << 8) + (b & 0xff)) val j: Long => Long = if (xs.length == 8) f compose g compose f else identity f(y ^ j(k)) } (g compose f compose g)(h) } }
13行目は、Redis のコードの big endian の場合の分岐に相当。
Byte 型を 0xff と論理積を取れば、非負の Int 値が手に入る。
実行例
scala> MurmurHash.murmurHash64A("A".getBytes).toHexString res1: String = fc089b66b14af040 scala> MurmurHash.murmurHash64A("AB".getBytes).toHexString res2: String = 24fb508dc42efb7f scala> MurmurHash.murmurHash64A("ABCDEFGHabcdefgh".getBytes).toHexString res3: String = 40055074d92b389f scala> MurmurHash.murmurHash64A(Seq.fill[Byte](100)(-1)).toHexString res4: String = 78be0c4f11cdc6d5
Redis の HyperLogLog で行われるカウント処理の結果も前回の調査内容と一致した。
scala> def count(x: Long): (Int, Int) = ( | (x & ((1L << 14) - 1)).toInt, | (14 until 64).find(i => ((x >>> i) & 1L) != 0L).getOrElse(63) - 13 | ) count: (x: Long)(Int, Int) scala> count(MurmurHash.murmurHash64A("A".getBytes)) res7: (Int, Int) = (12352,1) scala> count(MurmurHash.murmurHash64A("B".getBytes)) res8: (Int, Int) = (12964,3) scala> count(MurmurHash.murmurHash64A("C".getBytes)) res9: (Int, Int) = (4477,3)
0 件のコメント:
コメントを投稿