【密码学】一文读懂MurMurHash3

简介: 本文应该是MurMurHash算法介绍的最后一篇,来一起看一下最新的MurMurHash算法的具体过程,对于最新的算法来说,整个流程和之前的其实也比较相似,这里从维基百科当中找到了伪代码,也就不贴出来Google官方给出的推荐代码了,先来看一下维基百科给出的伪代码,这里只有32位的伪代码。

【密码学】一文读懂MurMurHash3


YXDGY[6I7JLU}U7U~W[L)]A.jpg

本文应该是MurMurHash算法介绍的最后一篇,来一起看一下最新的MurMurHash算法的具体过程,对于最新的算法来说,整个流程和之前的其实也比较相似,这里从维基百科当中找到了伪代码,也就不贴出来Google官方给出的推荐代码了,先来看一下维基百科给出的伪代码,这里只有32位的伪代码。

algorithm Murmur3_32 is
    // Note: In this version, all arithmetic is performed with unsigned 32-bit integers.
    //       In the case of overflow, the result is reduced modulo 232.
    input: key, len, seed
    c1 ← 0xcc9e2d51
    c2 ← 0x1b873593
    r1 ← 15
    r2 ← 13
    m ← 5
    n ← 0xe6546b64
    hash ← seed
    for each fourByteChunk of key do
        k ← fourByteChunk
        k ← k × c1
        k ← k ROL r1
        k ← k × c2
        hash ← hash XOR k
        hash ← hash ROL r2
        hash ← (hash × m) + n
    with any remainingBytesInKey do
        remainingBytes ← SwapToLittleEndian(remainingBytesInKey)
        // Note: Endian swapping is only necessary on big-endian machines.
        //       The purpose is to place the meaningful digits towards the low end of the value,
        //       so that these digits have the greatest potential to affect the low range digits
        //       in the subsequent multiplication.  Consider that locating the meaningful digits
        //       in the high range would produce a greater effect upon the high digits of the
        //       multiplication, and notably, that such high digits are likely to be discarded
        //       by the modulo arithmetic under overflow.  We don't want that.
        remainingBytes ← remainingBytes × c1
        remainingBytes ← remainingBytes ROL r1
        remainingBytes ← remainingBytes × c2
        hash ← hash XOR remainingBytes
    hash ← hash XOR len
    hash ← hash XOR (hash >> 16)
    hash ← hash × 0x85ebca6b
    hash ← hash XOR (hash >> 13)
    hash ← hash × 0xc2b2ae35
    hash ← hash XOR (hash >> 16)

这个入参和之前的两个算法都是一样的,依然可以看做是两个,第一个是需要哈希的值,第二个是seed。对于这个seed的取值,看了一下一些语言的实现,有些是0x0,有些是其他的值,目前也没发现推荐的值,如果有知道这个seed有什么取的方案的话,也欢迎和我交流。


算法过程

image.gifMurMurHash3算法结构

`P6C_TO3L[Z$2OL~4`$}7N9.png

这里依然稍微改了一下之前用到的图,也是一个出现了三次的老演员了,其实除了整个对消息的哈希处理过程当中,对于具体的哈希函数有一些差别,整个算法结构依然一样。好了到这里,有关MurMurHash算法的相关知识就水完了,恭喜我,又填完了一个坑。


代码实现

这里只给出32位的版本了,128位的就不写了,偷一个懒。

Rust

在查找相关代码的时候,这里学了rust的一个新的函数,这些代码主要是借鉴了参考资料里面的代码,他这个seed固定了另外一个值,原因未知,这里我改了一下,seed还是当参数吧。

use std::convert::TryInto;
use std::u32;
const C1: u32 = 0xcc9e_2d51;
const C2: u32 = 0x1b87_3593;
const D: u32 = 0xe654_6b64;
const FMIX1: u32 = 0x85eb_ca6b;
const FMIX2: u32 = 0xc2b2_ae35;
fn fmix32(mut h: u32) -> u32 {
    h ^= h >> 16;
    h = h.wrapping_mul(FMIX1);
    h ^= h >> 13;
    h = h.wrapping_mul(FMIX2);
    h ^= h >> 16;
    h
}
pub fn murmurhash3(key: &[u8], seed: u32) -> u32 {
    let mut h = seed;
    let mut chunks = key.chunks_exact(4);
    while let Some(chunk) = chunks.next() {
        let mut k: u32 = u32::from_le_bytes(chunk.try_into().unwrap());
        k = k.wrapping_mul(C1);
        k = k.rotate_left(15);
        k = k.wrapping_mul(C2);
        h ^= k;
        h = h.rotate_left(13);
        h = (h.wrapping_mul(5)).wrapping_add(D);
    }
    let remainder = chunks.remainder();
    match remainder.len() {
        3 => {
            let mut k = u32::from(remainder[2]) << 16;
            k ^= u32::from(remainder[1]) << 8;
            k ^= u32::from(remainder[0]);
            k = k.wrapping_mul(C1);
            k = k.rotate_left(15);
            k = k.wrapping_mul(C2);
            h ^= k;
        }
        2 => {
            let mut k = u32::from(remainder[1]) << 8;
            k ^= u32::from(remainder[0]);
            k = k.wrapping_mul(C1);
            k = k.rotate_left(15);
            k = k.wrapping_mul(C2);
            h ^= k;
        }
        1 => {
            let mut k = u32::from(remainder[0]);
            k = k.wrapping_mul(C1);
            k = k.rotate_left(15);
            k = k.wrapping_mul(C2);
            h ^= k;
        }
        _ => {}
    }
    fmix32(h ^ key.len() as u32)
}

Go

这里,用Go来在实现一下,就当学习了。

const C1 = 0xcc9e_2d51
const C2 = 0x1b87_3593
const D = 0xe654_6b64
const FMIX1 = 0x85eb_ca6b
const FMIX2 = 0xc2b2_ae35
func fmix32(h uint32) uint32 {
  h ^= h >> 16
  h *= FMIX1
  h ^= h >> 13
  h *= FMIX2
  h ^= h >> 16
  return h
}
func murmurhash3(key []byte, seed uint32) uint32 {
  h := seed
  keyLen := uint32(len(key))
  offset := 0
  e := binary.LittleEndian
  for keyLen >= 4 {
    k := e.Uint32(key[offset : offset+4])
    fmt.Println(k)
    k *= C1
    k = bits.RotateLeft32(k, 15)
    k *= C2
    h ^= k
    h = bits.RotateLeft32(h, 13)
    h *= 5
    h += D
    offset += 4
    keyLen -= 4
  }
  k1 := uint32(0)
  switch keyLen {
  case 3:
    k1 ^= uint32(key[offset+2]) << 16
    fallthrough
  case 2:
    k1 ^= uint32(key[offset+1]) << 8
    fallthrough
  case 1:
    k1 ^= uint32(key[offset])
    k1 *= C1
    k1 = bits.RotateLeft32(k1, 15)
    k1 *= C2
    h ^= k1
  }
  return fmix32(h ^ uint32(len(key)))
}


相关文章
|
Rust 算法 安全
【密码学】一文读懂MurMurHash2
上次我们聊过了一代的MurMurHash算法,是的,我又来水文章了,今天呢,接着来聊一下二代的MurMurHash算法,二代算法的整体结构实际上和一代算法差不太多,只是对于每一轮数据的处理过程当中的运算有一些差异,算法的来源依然是来自于Google官网给提供的源码,对着源码看的结构,对于这个算法呢,有两个版本,一个是32位的,一个是64位的,对于32位的算法和64位的算法,区别在于两个初始的魔数不同,整体运算过程还是十分相似的。
3165 0
【密码学】一文读懂MurMurHash2
|
Web App开发 Rust 算法
【密码学】一文读懂ChaCha20
好久没写新的加密算法的原理了, 这次所选取的加密算法结构比较简单, 一起来看一下吧。
10781 154
【密码学】一文读懂ChaCha20
|
Rust 算法 Go
【密码学】一文读懂FNV Hash
FNV哈希全名为Fowler-Noll-Vo算法,是以三位发明人Glenn Fowler,Landon Curt Noll,Phong Vo的名字来命名的,最早在1991年提出。它可以快速hash大量的数据并保持较小的冲突概率,适合hash一些相近的字符串比如IP地址、URL、文件名等等。目前FNV算法有三个版本,分别是: FNV-0(已废弃)、FNV-1以及FNV-1a。这三个算法的结构非常相似,因此呢,在这里就一块说了。
5181 0
【密码学】一文读懂FNV Hash
|
分布式数据库 Hbase 存储
带你读《HBase原理与实践》之一:HBase概述
Apache HBase是基于Apache Hadoop构建的一个高可用、高性能、多版本的分布式NoSQL数据库,是Google BigTable的开源实现,通过在廉价服务器上搭建大规模结构化存储集群,提供海量数据高性能的随机读写能力。
|
存储 算法 数据安全/隐私保护
【密码学】一文读懂白盒AES(Chow方案)(一)
本文主要参考了文献^[1], 代码参考了^[2], 这里感谢文献作者和代码作者,如果有能力的大佬,可以自行查看原文献,个人水平有限,有哪里写的不对的地方,也欢迎读者指正。
5338 0
【密码学】一文读懂白盒AES(Chow方案)(一)
|
JavaScript 前端开发 测试技术
在 golang 中执行 javascript 代码的方案详解
本文介绍了在 Golang 中执行 JavaScript 代码的四种方法:使用 `otto` 和 `goja` 嵌入式 JavaScript 引擎、通过 `os/exec` 调用 Node.js 外部进程以及使用 WebView 嵌入浏览器。每种方法都有其适用场景,如嵌入简单脚本、运行复杂 Node.js 脚本或在桌面应用中显示 Web 内容。
1436 15
在 golang 中执行 javascript 代码的方案详解
|
JSON Java 数据格式
Java将json中key值下划线转为驼峰格式
Java将json中key值下划线转为驼峰格式
2122 1
|
SQL 关系型数据库 MySQL
阿里面试:1000万级大表, 如何 加索引?
45岁老架构师尼恩在其读者交流群中分享了如何在生产环境中给大表加索引的方法。文章详细介绍了两种索引构建方式:在线模式(Online DDL)和离线模式(Offline DDL),并深入探讨了 MySQL 5.6.7 之前的“影子策略”和 pt-online-schema-change 方案,以及 MySQL 5.6.7 之后的内部 Online DDL 特性。通过这些方法,可以有效地减少 DDL 操作对业务的影响,确保数据的一致性和完整性。尼恩还提供了大量面试题和解决方案,帮助读者在面试中充分展示技术实力。
|
JavaScript 算法 前端开发
国标哈希算法基础:SHA1、SHA256、SHA512、MD5 和 HMAC,Python和JS实现、加盐、算法魔改
国标哈希算法基础:SHA1、SHA256、SHA512、MD5 和 HMAC,Python和JS实现、加盐、算法魔改
2163 1
|
存储 SQL 消息中间件
ClickHouse表引擎到底怎么选
表引擎在ClickHouse中的作用十分关键,直接决定了数据如何存储和读取、是否支持并发读写、是否支持index、支持的query种类、是否支持主备复制等。
7020 2