murmur 算法

简介

MurmurHash是一种高效的非加密哈希函数,适用于哈希表中的一般哈希任务。
MurmurHash的名称来源于Murmur,意为一种低频的声音,体现了其设计的低碰撞率和高性能。
名称来自两个基本操作,乘法(MU)和旋转(R),在其内部循环中使用。与其它流行的哈希函数相比,对于规律性较强的key,MurmurHash的随机分布特征表现更良好。
MurmurHash与加密散列函数不同,它不是专门设计为难以被对手逆转,因此不适用于加密目的。它常被应用于分布式系统,很多开源项目如Kafka、Redis,Memcached,Cassandra,HBase,Elasticsearch等等都使用它。
MurmurHash的当前的版本是MurmurHash3,能够产生出32-bit或128-bit哈希值。

优点和缺点

速度快,比安全散列算法快几十倍;
变化足够激烈,相似的字符串如“abc”和“abd”能够均匀散落在哈希环上;
高熵:确保输入的微小变化会显著改变输出,减少碰撞。
高性能:利用简单的位操作和混合步骤,适用于现代处理器。
确定性:相同的输入总是生成相同的输出。

不保证安全性(缺点)

算法原理

以MurmurHash3_x86_32为例,它适用于32位系统,并输出32位的哈希值。下面是MurmurHash3的主要步骤:

初始化

设置一个种子(seed)值,用于初始化哈希值。这样可以通过不同的种子来生成不同的哈希值。

处理块(chunks)

输入数据被分成固定大小的块(通常为4 bytes)。每个块使用一次哈希函数。
对于每个块,首先将它们视为32位整数。

混合过程:

对每个32位块进行一系列位操作,包括乘法、左移和右移。这些操作用来混合位,使得输入的不同位对最终哈希值有较大的影响。
具体的混合步骤如下:

k *= c1;
k = rotl32(k, r1);
k *= c2;
h ^= k;
h = rotl32(h, r2);
h = h * m + n;

c1, c2, r1, r2, m, n是固定的常数,通过实验选择,使得哈希函数具有良好的分布性和随机性。

处理尾部(tail)

如果输入数据的长度不是块大小的倍数,剩余的未处理字节(称为尾部)也会影响最终哈希值。
对尾部的字节进行类似的混合处理,但处理量要少得多

最终化(finalization)

h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;

Demo

#include <stdio.h>
#include <stdint.h>
#include <string.h>

#define ROTL32(x, r) ((x << r) | (x >> (32 - r)))

uint32_t MurmurHash3_x86_32(const void *key, int len, uint32_t seed) {
    const uint8_t *data = (const uint8_t *)key;
    const int nblocks = len / 4;
    uint32_t h1 = seed;
    
    const uint32_t c1 = 0xcc9e2d51;
    const uint32_t c2 = 0x1b873593;

    // Body
    const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4);
    for (int i = -nblocks; i; i++) {
        uint32_t k1 = blocks[i];

        k1 *= c1;
        k1 = ROTL32(k1, 15);
        k1 *= c2;

        h1 ^= k1;
        h1 = ROTL32(h1, 13);
        h1 = h1 * 5 + 0xe6546b64;
    }

    // Tail
    const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
    uint32_t k1 = 0;

    switch (len & 3) {
    case 3:
        k1 ^= tail[2] << 16;
    case 2:
        k1 ^= tail[1] << 8;
    case 1:
        k1 ^= tail[0];
        k1 *= c1;
        k1 = ROTL32(k1, 15);
        k1 *= c2;
        h1 ^= k1;
    }

    // Finalization
    h1 ^= len;
    h1 ^= h1 >> 16;
    h1 *= 0x85ebca6b;
    h1 ^= h1 >> 13;
    h1 *= 0xc2b2ae35;
    h1 ^= h1 >> 16;

    return h1;
}

int main() {
    const char *key = "Hello, World!";
    uint32_t seed = 42;  // A random seed value
    uint32_t hash = MurmurHash3_x86_32(key, strlen(key), seed);

    printf("Hash of '%s' with seed %u is: %u\n", key, seed, hash);
	/* Hash of 'Hello, World!' with seed 42 is: 1794106050 */
    return 0;
}
MurmurHash 是一种非加密型哈希函数,专为高效哈希检索操作而设计,具有良好的随机分布特性,尤其在处理规律性较强的键值时表现优异。该算法由 Austin Appleby 于 2008 年发明,并发展出多个版本,所有版本均已发布到公有领域,可自由使用[^1]。 ### MurmurHash 的核心原理 MurmurHash 的核心思想是通过一系列位操作和乘法运算来混合输入数据,以生成一个分布均匀的哈希值。以 MurmurHash2 的实现为例,其主要步骤包括: 1. **初始化哈希值**:使用种子值(seed)和输入数据长度的异或操作来初始化哈希值。 2. **分块处理数据**:每次处理 4 字节的数据块,将其转换为一个 32 位整数 `k`,然后通过乘法和位移操作进行混合。 3. **处理剩余字节**:对于不足 4 字节的部分,根据剩余字节数进行单独处理,将这些字节按位插入到哈希值中。 4. **最终混合**:对哈希值进行几次额外的位移和乘法操作,以确保最后的几个字节能够充分融入哈希结果中[^3]。 以下是 MurmurHash2 的核心代码片段: ```c uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ) { const uint32_t m = 0x5bd1e995; const int r = 24; uint32_t h = seed ^ len; const unsigned char * data = (const unsigned char *)key; while(len >= 4) { uint32_t k = *(uint32_t*)data; k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; data += 4; len -= 4; } switch(len) { case 3: h ^= data[2] << 16; case 2: h ^= data[1] << 8; case 1: h ^= data[0]; h *= m; }; h ^= h >> 13; h *= m; h ^= h >> 15; return h; } ``` ### MurmurHash 的使用场景 MurmurHash 由于其高效的计算速度和良好的分布特性,广泛应用于以下场景: 1. **哈希表**:MurmurHash 适用于构建哈希表,特别是在需要快速查找和插入的场景中。它的均匀分布特性可以减少哈希冲突,提高哈希表的性能。 2. **布隆过滤器(Bloom Filter)**:在布隆过滤器中,MurmurHash 可以用作多个独立哈希函数之一,用于判断一个元素是否可能存在于集合中。 3. **一致性哈希(Consistent Hashing)**:在分布式系统中,MurmurHash 可以用于生成一致性哈希环上的节点位置,从而实现负载均衡。 4. **数据分片(Data Sharding)**:在大数据处理中,MurmurHash 可以用于将数据均匀地分配到不同的分片中,确保数据分布的平衡性。 5. **缓存系统**:MurmurHash 可以用于生成缓存键的哈希值,帮助缓存系统快速定位存储位置。 ### MurmurHash 在实际开发中的应用示例 在实际开发中,MurmurHash 也可以与其他编码方式结合使用,例如将哈希值转换为 62 进制字符串,以生成更短且易于管理的标识符。以下是一个简单的 Java 实现示例,展示了如何使用 MurmurHash 并将其结果转换为 62 进制字符串: ```java import com.google.common.hash.Hashing; public class HashUtil { private static final String CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static long murmurHash32(String param) { return Hashing.murmur3_32().hashUnencodedChars(param).padToLong(); } private static String encodeToBase62(long num) { StringBuilder sb = new StringBuilder(); do { int i = (int) (num % 62); sb.append(CHARS.charAt(i)); num /= 62; } while (num > 0); return sb.reverse().toString(); } public static String generateShortHash(String input) { long hash = murmurHash32(input); return encodeToBase62(hash); } } ``` 上述代码中,`murmurHash32` 方法使用了 Guava 库中的 `Hashing.murmur3_32()` 来生成 32 位的 MurmurHash 值,而 `encodeToBase62` 方法则将该哈希值转换为 62 进制字符串,从而生成更短且易于读写的标识符。 ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值