From be0abac555fcc8644ea8c190a7003122ffeddc90 Mon Sep 17 00:00:00 2001 From: XiaoqiangWu <39110174+wuxiaoqiang12@users.noreply.github.com> Date: Thu, 15 Dec 2022 11:22:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=AD=97=E7=AC=A6=E4=B8=B2?= =?UTF-8?q?=E5=93=88=E5=B8=8C=E7=9A=84=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 完善字符串哈希部分的操作步骤描述 --- docs/chapter_hashing/hash_map.md | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index 793d90323..9643ce556 100644 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -300,3 +300,54 @@ $$ - 尽量少地发生哈希冲突; - 时间复杂度 $O(1)$ ,计算尽可能高效; - 空间使用率高,即 “键值对占用空间 / 哈希表总占用空间” 尽可能大; + +## 常用哈希函数 + +下面介绍两种常见的哈希类型,数字哈希和字符串哈希。 + +### 数字哈希 + +对于需要将数字哈希化的场景,常采用对大质数取模的方式作为哈希函数以此来减少冲突。若用 $N$ 来表示所选取的大质数,则哈希函数 $f(x)$ 可表达为 + +$$ +f(x) = (x (\mod N) + N)(\mod N) +$$ + +该函数在第一次取模后还有一次加 $N$ 的操作是因为在某些编程语言如 C/C++ 中负数取模会得到负数,这与数学中的取模运算定义不同(数学定义中无论正负数取模的结果均为正数)。另外,我们常常将取模后的值作为数组中的索引,这样正数也能保证数组索引的正确性。 + +大质数的选择取决于你希望哈希后的长度,若一堆数字你希望哈希后分布在 $0-n$ 之间,则 $N$ 选择大于 $n$ 的大质数即可。选择算法示例如下: + +=== "C" + + ```c title="prime_num_sel.c" + int main() { + for (int i = 1000; ;i++) { + int flag = 0; + for (int j = 2; j < i; j++) { + if (i % j == 0) { + flag = 1; + break; + } + } + if (flag == 0) { + printf("the prime number is %d", i); + break; + } + } + return 0; + } + ``` + +### 字符串哈希 + +这里介绍一种被称作“字符串前缀哈希”的算法来实现字符串的哈希化。若有一字符串 `str = "abcedacbd"`,则哈希表中的元素表示从左边起到第 `i` 个位置上的子字符串的哈希值。即,`f(1) -> "a"; f(2) -> "ab"; f(3) -> "abc"`,以此类推。 + +具体过程如下 + +1. 将字符串存储在下标为 `1` 的 `char` 数组中; +2. 计算字符串的前缀数字,将他们哈希化,将字符串看作是一个 `P` 进制数,这里 `P = 131` 或者 `P = 13331`。按照经验,这两个值对字符串哈希会产生非常小的冲突可能。 +3. 将这个 `P` 进制数转换为10进制,通常转换出来的数字会很大,则将其对 `Q` 取模。这样任意一个字符串都可以转换成 $0 \cdots Q - 1$ 之间的数字:$(1234)_P => (1 * P^3 + 2 * P^2 + 3 * P^1 + 4 * P^0) \mod Q$ +4. 对于`Hash`函数,将计算字符串中从 `l` 到 `r` 的哈希值,通过前缀哈希表以及下公式计算,注意左侧为低位,右侧为高位。 + +`f(r) - f(l - 1) * p[r - l + 1]` ,其中 `p` 数组存放了从 $P^0 \sim P^N$ 的所有数字,方便查询。 +