简介

  • MurmurHash 是一种非加密型哈希函数(Non-cryptographic hash function),适用于一般的哈希检索操作。与其它流行的哈希函数相比,对于规律性较强的 key,MurmurHash 的随机分布特征表现更良好。
  • 常见的 MD5SHA1 是加密型哈希函数(Cryptographic hash function

Hash 算法评价

杨保华《区块链·原理、设计与应用》第5章密码学与安全技术中讲到,一个优秀的 Hash 算法将能实现如下功能:

  • 正向快速

    给定明文和 Hash 算法,在有限时间和有限资源内能计算得到 Hash 值
  • 逆向困难

    给定(若干) Hash 值,在有限时间内很难(基本不可能)逆推出明文;
  • 输入敏感

    原始输入信息发生任何改变,新产生的 Hash 值都应该出现很大不同;
  • 冲突避免

    很难找到两段内容不同的明文,使得它们的 Hash 值一致(发生碰撞)。
    冲突避免有时候又称为“抗碰撞性”,分为“弱抗碰撞性”和“强抗碰撞性”。如果给定明文前提下,无法找到与之碰撞的其他明文,则算法具有“弱抗碰撞性”;如果无法找到任意两个发生 Hash 碰撞的明文,则称算法具有“强抗碰撞性”。

家族成员

MurmurHash1, MurmurHash2, MurmurHash3

64bit long

Python3(mmh3

# signed
>>> mmh3.hash64('CN305183362S')
(-1993453447204570204, -4896406162740467071)
# unsigned
>>> mmh3.hash64('CN305183362S', signed=False)
(16453290626504981412, 13550337910969084545)
>>> mmh3.hash('CN305183362S')
2088819274

Java(guava

import com.google.common.hash.Hashing;
import com.google.common.primitives.UnsignedLong;
import static com.google.common.base.Charsets.UTF_8;
public class Main {
    public static void main(String[] args) {
        long signed = Hashing.murmur3_128().hashString("CN305183362S", UTF_8).asLong();
        UnsignedLong unsigned = UnsignedLong.fromLongBits(signed);
        System.out.println(signed);     // -1993453447204570204
        System.out.println(unsigned);   // 16453290626504981412
        int hash32 = Hashing.murmur3_32().hashString("CN305183362S", UTF_8).asInt();
        System.out.println(hash32);     // 2088819274
    }
}

计算 mmh3 十六进制字符串

Python3

>>> import binascii
>>> import mmh3
>>> binascii.b2a_hex(mmh3.hash_bytes('CN305183362S')).decode('utf8')
'a4fb17cba6d455e4812ad28989780cbc'    # 32个字符,128 bit
>>> hex(mmh3.hash128('CN305183362S'))
'0xbc0c788989d22a81e455d4a6cb17fba4'

Java

  • 借用 BigInteger

    import java.math.BigInteger;
    import org.apache.commons.codec.digest.MurmurHash3;
    import org.apache.commons.lang.ArrayUtils;
    public class AppTester {
      public static void main(String[] args) {
          final byte[] origin = "CN305183362S".getBytes();
          long[] vec = MurmurHash3.hash128(origin, 0, origin.length, 0);
          // 将 long 转换为 BigInteger
          BigInteger bigInteger0 = BigInteger.valueOf(vec[0]);
          BigInteger bigInteger1 = BigInteger.valueOf(vec[1]);
          // 将 BigInteger 转换为 byte[]
          byte[] array0 = bigInteger0.toByteArray();
          byte[] array1 = bigInteger1.toByteArray();
          // 反转 byte[](大小端转换)
          ArrayUtils.reverse(array0);
          ArrayUtils.reverse(array1);
          // 将 byte[] 转换为无符号整数,并转为十六进制字符串
          String part0 = (new BigInteger(1, array0)).toString(16);
          String part1 = (new BigInteger(1, array1)).toString(16);
          System.out.println(part0 + part1); // a4fb17cba6d455e4812ad28989780cbc
      }
    }
  • 借用 ByteBuffer(好文推荐:Java 中 byte、byte 数组和 int、long 之间的转换

    import java.nio.ByteBuffer;
    import org.apache.commons.codec.digest.MurmurHash3;
    import org.apache.commons.lang.ArrayUtils;
    public class AppTester {
      public static void main(String[] args) {
          final byte[] origin = "CN305183362S".getBytes();
          long[] vec = MurmurHash3.hash128(origin, 0, origin.length, 0);
          ByteBuffer buf0 = ByteBuffer.allocate(8); 
          ByteBuffer buf1 = ByteBuffer.allocate(8);         
          buf0.putLong(0, vec[0]);
          buf1.putLong(0, vec[1]);        
          byte[] array0 = buf0.putLong(0, vec[0]).array();
          byte[] array1 = buf1.putLong(0, vec[1]).array();        
          ArrayUtils.reverse(array0);    // 反转 byte[](大小端转换)
          ArrayUtils.reverse(array1);    // 反转 byte[](大小端转换)
          buf0.put(array0, 0, array0.length).flip();
          buf1.put(array1, 0, array1.length).flip();        
          String part0 = String.format("%x", buf0.getLong());
          String part1 = String.format("%x", buf1.getLong());
          System.out.println(part0 + part1);    // a4fb17cba6d455e4812ad28989780cbc
      }
    }
  • 浅显易懂版,不用处理字节

    import org.apache.commons.codec.digest.MurmurHash3;
    public class AppTester {
      public static void main(String[] args) {
          final byte[] origin = "CN305183362S".getBytes();
          long[] vec = MurmurHash3.hash128(origin, 0, origin.length, 0);
          String part0 = String.format("%x", vec[0]);
          String part1 = String.format("%x", vec[1]);
          String line = "";
          // 反转前半段(大小端转换)
          for (int i = 0; i < 8; ++i) {
              line += part0.substring(14 - 2 * i, 16 - 2 * i);
          }
          // 反转后半段(大小端转换)
          for (int i = 0; i < 8; ++i) {
              line += part1.substring(14 - 2 * i, 16 - 2 * i);
          }
          System.out.println(line);    // a4fb17cba6d455e4812ad28989780cbc
      }
    }
  • google guava (推荐)

    import com.google.common.hash.Hashing;
    import static com.google.common.base.Charsets.UTF_8;
    public class Main {
      public static void main(String[] args) {
          String mmh3 = Hashing.murmur3_128().hashString("CN305183362S", UTF_8).toString();
          System.out.println(mmh3);   // a4fb17cba6d455e4812ad28989780cbc
      }
    }

Node.js

  • MurmurHash3js
  • 在线测试: http://murmurhash.shorelabs.com/

    var murmurHash3 = require("murmurhash3js");
    let line = murmurHash3.x64.hash128("CN305183362S");
    console.log(line);        // e455d4a6cb17fba4bc0c788989d22a81
    let newLine = "";
    // 反转前半段(大小端转换)
    for (let i = 0; i < 8; ++i) {
      newLine += line.slice(14-2*i, 16-2*i);
    }
    // 反转后半段(大小端转换)
    for (let i = 0; i < 8; ++i) {
      newLine += line.slice(30-2*i, 32-2*i);
    }
    console.log(newLine);    // a4fb17cba6d455e4812ad28989780cbc
本文出处 qbit snap

qbit
268 声望279 粉丝