Scala: MurmurHash を実装する
Redis の MurmurHash2 実装をそのまま Scala で書いてみる。
コード
効率よりも分かりやすさを優先。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | object MurmurHash { private val defaultSeed = 0xadc83b19 L def murmurHash 64 A(data : Seq[Byte], seed : Long = defaultSeed) : Long = { val m = 0xc6a4a7935bd1e995 L 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( 0 L)((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 値が手に入る。
実行例
1 2 3 4 5 6 7 8 9 10 11 | scala> MurmurHash.murmurHash 64 A( "A" .getBytes).toHexString res 1 : String = fc 089 b 66 b 14 af 040 scala> MurmurHash.murmurHash 64 A( "AB" .getBytes).toHexString res 2 : String = 24 fb 508 dc 42 efb 7 f scala> MurmurHash.murmurHash 64 A( "ABCDEFGHabcdefgh" .getBytes).toHexString res 3 : String = 40055074 d 92 b 389 f scala> MurmurHash.murmurHash 64 A(Seq.fill[Byte]( 100 )(- 1 )).toHexString res 4 : String = 78 be 0 c 4 f 11 cdc 6 d 5 |
Redis の HyperLogLog で行われるカウント処理の結果も前回の調査内容と一致した。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | scala> def count(x : Long) : (Int, Int) = ( | (x & (( 1 L << 14 ) - 1 )).toInt, | ( 14 until 64 ).find(i = > ((x >>> i) & 1 L) ! = 0 L).getOrElse( 63 ) - 13 | ) count : (x : Long)(Int, Int) scala> count(MurmurHash.murmurHash 64 A( "A" .getBytes)) res 7 : (Int, Int) = ( 12352 , 1 ) scala> count(MurmurHash.murmurHash 64 A( "B" .getBytes)) res 8 : (Int, Int) = ( 12964 , 3 ) scala> count(MurmurHash.murmurHash 64 A( "C" .getBytes)) res 9 : (Int, Int) = ( 4477 , 3 ) |
0 件のコメント:
コメントを投稿