为什么Hash函数 H(k) = k % m中 m 尽量不要为2的幂次 也不是要是2^i -1

为什么Hash函数 H(k) = k % m中 m 尽量不要为2的幂次

下面的截屏来自CLRS的11章 关于哈希函数的讨论

之前我就一直困惑,为什么

When using the division method, we usually avoid certain values of m. For example, m should not be a power of 2, since ifm = 2^p
, then H(k) is just the p lowest-order bits of
k. Unless we know that all low-order p-bit patterns are equally likely, we are better off designing the hash function to depend on all the bits of the key.

-----------------------------------------------------------------------------------------------------------

为嘛就是尽量要避免 k%m中m不要是2的幂次呢?

如果2的i次幂 2^i = 10... .... 0 从1后面的第一个0开始到结束,一共有i个0

如果用k%m进行取余数操作,m = 2^i, 结果就是把k 截断保留低位的i位。这对于hash来说,是很“糟糕的特性”

问题的根结在于hash要有很好的特性,就是要避免碰撞,避免碰撞就要分布均匀的插入

直接的截断是很粗鲁的方式,无法保证插入数据能够均匀的分布于hash table中。

-----------------------------------------------------------------------------------------------------------

为嘛就是要避免k %m 中m不要是2幂次-1呢(2^i -1)?

对于不同的字符串S1 = “abcd” S2 = "adcb"

他们的hash值是相同的!但是他们是不同的字符串!他们会冲突!

怎么办呢?考虑字符串中的单个字符的顺序,对各个字符串进行加权,而加权的具体方式就是他们所处于字符串中的位。比方说字符串

S1 求值可以这样 ‘a‘*2^(0) + ‘b‘* (2^(1)) + ‘c‘ * (2^2) + ‘d‘ * (2^3)

S2 求值可以这样 ‘a‘*2^(0) + ‘d‘* (2^(1)) + ‘c‘ * (2^2) + ‘b‘ * (2^3)

两者的字面值就不一样了。这样就完了?可以确保不会冲突? 没完呢。。。

证明很酷帅

时间: 2024-11-05 20:46:38

为什么Hash函数 H(k) = k % m中 m 尽量不要为2的幂次 也不是要是2^i -1的相关文章

密码学Hash函数

定义: Hash函数H将可变长度的数据块M作为输入,产生固定长度的Hash值h = H(M). 称M是h的原像.因为H是多对一的映射,所以对于任意给定的Hash值h,对应有多个原像.如果满足x≠y且H(x)=H(y),则称为碰撞. 应用: 用于验证数据的完整性,即判断数据是否被篡改过. 密码学Hash函数的定义: 在安全应用中使用的Hash函数. 密码学Hash函数的应用: 1.消息认证 Hash码能够通过如下不同方法用于提供消息认证 a) 使用对称密码E加密消息和Hash码,由于只有A和B共享

密码学hash函数-SHA256-512

[latexpage] Hash函数又称哈希函数.散列函数.杂凑函数.它是一种单向密码体制,即从一个从明文到密文的不可逆映射,只有加密过程,没有解密过程. Hash函数H将可变长度的数据块M作为输入,产生固定长度的Hash值h=H(M). 在安全应用中使用的Hash函数称为密码学Hash函数.(单向性).(抗碰撞性) 弱抗碰撞性:给定一个消息M,要找到另一个消息M',使得H(M)=H(M')很难. 强抗碰撞性:要找到两个随机明文M和M',使得H(M)=H(M')很难. Hash函数特点: 1.

实现一个函数,可以左旋字符串中的k个字符

实现一个函数,可以左旋字符串中的k个字符. AABCD左旋一个字符得到ABCDA AABCD左旋两个字符得到BCDAA #include<stdio.h>  #include<stdlib.h>  void spin(char arr[],int num) //spin函数用以完成旋转字符的功能  {  char arr1[5] = {0};  char *str = arr; //创建两个指针都指向原字符数组的首地址  char *start = arr;  char *mov 

编写一个函数,可以左旋字符串中k个字符

题目: 实现一个函数,可以左旋字符串中的k个字符. 例如: abcdef左旋一个字符得到bcdefa abcdef左旋两个字符得到cdefab 题目分析: 对于这个问题,可以用很多种方法求解,这里介绍两种方法: 算法一: 左旋字符串的k个字符,我们可以先将剩下的n-k个字符移动最前面,然后将左旋的k个字符移动到字符串的最后面的位置,这就完成了字符串左旋k个字符的功能.其中,对于n-k个字符的移动,可以利用while循环,将后面的字符逐一进行拷贝.下面是具体的程序: #define _CRT_SE

实现一个函数,可以左旋字符串中的k个字符。

题目:实现一个函数,可以左旋字符串中的k个字符. ABCD左旋一个字符得到BCDA ABCD左旋两个字符得到CDAB 方法一:直接旋转void left_move(chararr, int k)//左旋字符串{char tmp = 0;charp = arr;while (k!=0){tmp =p;while ((p+1) != '\0'){p = (p + 1);p++;}*p = tmp;k--;}} 方法二:void left_move2(char*arr, int k)//更优解法//要

【c语言】 编写一个函数实现n^k,使用递归实现

//编写一个函数实现n^k,使用递归实现 #include <stdio.h> int cifang( int x, int y )//2^3=2*2*2 { int sum = 0; if( y == 0 ) sum = 1; else sum = x * cifang( x, ( y - 1 ) ); return sum; } int main() { printf("%d\n",cifang( 2,0 )); printf("%d\n",cifa

【C语言】编写一个函数实现n^k,使用递归实现。

//编写一个函数实现n^k,使用递归实现 #include <stdio.h> int fun(int n,int k) //求n的k次方 { int sum; if (k==0) { sum=1; } else { sum=n*fun(n,k-1); } return sum; } int main () { printf("%d\n",fun(2,3)); return 0; }

POJ 3294 后缀数组:求不小于k个字符串中的最长子串

思路:先把所有的串连接成一个串,串写串之前用没出现过的字符隔开,然后求后缀:对height数组分组二分求得最长的公共前缀,公共前缀所在的串一定要是不同的,不然就不是所有串的公共前缀了,然后记下下标和长度即可. 刚开始理解错题意,然后不知道怎么写,然后看别人题解也不知道怎么意思,后面看了好久才知道题目意思理解错了. 时间四千多ms,别人才一百多ms,不知道别人怎么做的-- #include<iostream> #include<cstdio> #include<cstring&

【POJ 3294】Life Forms 不小于k个字符串中的最长子串

一下午和一晚上都在刚这道题,各种错误都集齐了so sad 我的时间啊!!! 后缀数组就先做到这里吧,是在伤不起啊QAQ 出现了各种奇怪的错误,看了标算,然后乱改自己的代码,莫名其妙的改A了,后来发现用字符直接给int赋值会WA,必须一个字符先给另一个字符赋值,后者再给int赋值就能A(什么鬼).后来加了一个(int)s[n]强制转换就简单地A了,评测时强制转换睡觉了吗?还是我rp太差,得多攒点rp #include<cstdio> #include<cstring> #includ