Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >为什么哈希函数应该使用素数模数?

为什么哈希函数应该使用素数模数?
EN

Stack Overflow用户
提问于 2009-07-17 19:30:05
回答 17查看 110.1K关注 0票数 374

很久以前,我花了1.25美元买了一本廉价的数据结构书。在这篇文章中,散列函数的解释说,由于“数学的本质”,它最终应该由一个质数来修改。

你对一本1.25美元的书有什么期望?

不管怎么说,我花了很多年来思考数学的本质,但还是搞不清楚。

当桶的数量是质数时,数字的分布是否真的更均匀?

或者这是一个老程序员的故事,每个人都接受,因为其他人都接受它?

EN

回答 17

Stack Overflow用户

回答已采纳

发布于 2009-07-18 02:43:06

通常,一个简单的散列函数的工作原理是将输入的“组成部分”(字符串中的字符)乘以某个常量的幂,然后以某种整数类型将它们相加。因此,例如,一个典型的(虽然不是特别好的)字符串散列可能是:

代码语言:javascript
运行
AI代码解释
复制
(first char) + k * (second char) + k^2 * (third char) + ...

然后,如果输入了一串具有相同第一个字符的字符串,那么结果都将是相同的模k,至少在整数类型溢出之前是这样。

例如,使用k=31,Java的字符串hashCode与此非常相似-它颠倒了字符的顺序。所以你得到了相同结尾的字符串之间模数为31的显著关系,以及除了结尾附近之外相同的字符串之间的模数为2^32的显著关系。这不会严重扰乱哈希表的行为。

哈希表的工作原理是取散列的模除以存储桶的数量。

在哈希表中,重要的是不要在可能的情况下产生冲突,因为冲突会降低哈希表的效率。

现在,假设有人将一大堆值放到一个哈希表中,这些值在项之间有一些关系,比如所有项都具有相同的第一个字符。我想说,这是一个相当可预测的使用模式,所以我们不希望它产生太多的冲突。

事实证明,“由于数学的本质”,如果散列中使用的常量和桶的数量是coprime,那么在一些常见情况下冲突就会最小化。如果它们不是coprime,那么在输入之间存在一些不会最小化冲突的相当简单的关系。所有的散列都是以公因子为模的,这意味着它们都会落入以公因子为模的值的第1/n个存储桶中。你会得到n倍的碰撞,其中n是公因子。由于n至少是2,我想说对于一个相当简单的用例来说,生成至少是正常情况的两倍的冲突是不可接受的。如果一些用户要将我们的发行版分成几个桶,我们希望它是一个奇怪的意外,而不是一些简单的可预测的使用。

现在,哈希表实现显然无法控制放入其中的项。他们不能阻止他们之间的联系。因此,要做的事情是确保常数和桶计数是互质的。这样,您就不会仅仅依靠“最后”组件来确定相对于某个小公因子的桶的模数。据我所知,他们不一定要是最好的,只要是共同的。

但是如果哈希函数和哈希表是独立编写的,那么哈希表就不知道哈希函数是如何工作的。它可能会使用一个带有小因子的常量。如果你幸运的话,它的工作方式可能完全不同,而且是非线性的。如果散列足够好,那么任何存储桶计数都可以。但是一个偏执的哈希表不能假设一个好的哈希函数,所以应该使用质数的桶。类似地,偏执的散列函数应该使用较大的素数常量,以减少某人使用许多碰巧与常量有公因子的桶的机会。

在实践中,我认为使用2的幂作为存储桶的数量是相当正常的。这是方便的,并且省去了不得不到处搜索或预先选择正确大小的质数。因此,您依赖于哈希函数而不使用乘法器,这通常是一个安全的假设。但是,您仍然可以根据上面的哈希函数偶尔得到糟糕的哈希行为,而主存储桶计数可能会进一步帮助您。

据我所知,提出“一切都必须是质数”的原则是在哈希表上良好分布的充分条件,但不是必要条件。它允许每个人进行互操作,而不需要假设其他人遵循相同的规则。

编辑:使用质数存储桶还有另一个更专业的原因,那就是如果您使用线性探测来处理冲突。然后从哈希码计算步幅,如果步幅是存储桶计数的一个因素,那么您只能在返回开始之前执行(bucket_count / stride)探测。您最希望避免的情况是stride = 0,当然,它必须是特殊大小写的,但为了避免特殊大小写的bucket_count / stride等于一个小整数,您可以将bucket_count设为素数,而不关心步长是什么,只要它不是0。

票数 270
EN

Stack Overflow用户

发布于 2009-09-22 22:58:18

在从哈希表中插入/检索时,您要做的第一件事是计算给定键的hashCode,然后通过执行hashCode % table_length将hashCode修剪为hashTable的大小,从而找到正确的存储桶。下面是你很可能在某处读过的两个“陈述”

  1. 如果对table_length使用2的幂,查找(hashCode(key) % 2^n )就像(hashCode(key) & (2^n -1))一样简单快速。但是如果你计算给定键的hashCode的函数不好,你肯定会在几个散列桶中聚集许多键。
  2. 但是如果你对table_length使用质数,hashCodes计算的可能会映射到不同的散列桶中,即使你有一个稍微愚蠢的hashCode函数。

这就是证据。

假设您的hashCode函数在{x,2x,3x,4x,5x,6x...}中产生以下hashCodes,那么所有这些都将聚集在m个bucket中,其中m= table_length/GreatestCommonFactor(table_length,x)。(验证/派生这一点很简单)。现在,您可以执行以下操作之一来避免集群

确保你不会像{x,2x,3x,4x,5x,6x...}.But那样生成太多的hashCodes,它们是另一个hashCode的倍数。如果你的hashTable有几百万个条目,这可能会有点困难。或者简单地通过使table_length (GreatestCommonFactor,x)等于1来使m等于table_length,即通过使table_length与x互质。如果x可以是任意数字,则确保table_length是质数。

发件人- http://srinvis.blogspot.com/2006/07/hash-table-lengths-and-prime-numbers.html

票数 33
EN

Stack Overflow用户

发布于 2009-07-17 11:33:28

http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/

非常清楚的解释,还有图片。

编辑:作为总结,使用质数是因为当将值乘以所选的质数并将它们相加时,您获得唯一值的机会最大。例如,给定一个字符串,将每个字母值与质数相乘,然后将这些值相加,就会得到它的散列值。

一个更好的问题应该是,为什么数字到底是31?

票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1145217

复制
相关文章
Lambda表达式
Lambda表达式(Lambda Expression)是C#中一种特殊语法,它的引入,使得匿名方法更加简单易用,最直接的是在方法体内调用代码或者为委托传入方法体的形式与过程变得更加优雅。
宿春磊Charles
2022/03/29
2260
Lambda表达式
从2014年java8发布到现在已经有几个年头了,现在java11都发布了。公司最近把服务器环境重新搭建了一遍,jdk版本也从7换成了8,终于可以在代码里面写Lambda表达式了。作为一名java开发人员,java8的一些新东西也是必须要掌握的,今天就说说这Lambda表达式的使用。
用户4283147
2022/10/08
4720
Lambda表达式
Lambda表达式
如当我们需要依次打印某集合的内容(如水果名字集合),之前我们使用 for 循环来完成打印,但使用Lambda表达式则可以:
摸鱼的G
2023/02/22
2810
Lambda表达式
Lambda表达式
Java中一切皆对象,因此在Java中函数或者方法无法独立存在,它们不是一个对象,要想像JavaScript进行函数式编程,Java8提出Lambda表达式。
Krains
2020/08/19
3850
Lambda表达式
这里我们添加了一些自定义代码到 Schedule 监听器中,需要先定义匿名内部类,然后传递一些功能到 onSchedule 方法中。
一觉睡到小时候
2019/09/25
7040
Lambda表达式
Lambda表达式
Lambda表达式是可以在函数式接口上使用的。函数式接口就是只定义一个抽象方法的接口。比如:
后端码匠
2019/09/02
6030
lambda表达式
不止于python
2023/09/05
1530
lambda表达式
lambda表达式在实际开发中的使用
作为写代码已经两年的程序员了,lambda已经是再熟悉不过了。其实在众多的编程语言中,python javascript java中都有lambda的影子。包括比较新的编程语言golang,到最后发现其实各种语言的语法和特性都是相互抄袭的,所以在接触新技术的时候,很容易触类旁通。
shigen
2023/10/06
2280
lambda表达式在实际开发中的使用
Lambda表达式
当需要启动一个线程去完成任务时,通常会通过 java.lang.Runnable 接口来定义任务内容,并使用java.lang.Thread 类来启动该线程。代码如下:
咕咕星
2020/08/19
4850
Lambda表达式
lambda表达式
        一般来说函数是承担着需求实现的重要内聚的组件,而函数内部的回调函数又达到解耦的作用,在对于后期的维护修改和他人的阅读都起到了积极的作用。        
比特大冒险
2023/05/09
2380
lambda表达式
lambda表达式
Lambda表达式的语法 基本语法: (parameters) -> expression 或 (parameters) ->{ statements; } 下面是Java lambda表达式的简单例子: [java] view plaincopy // 1. 不需要参数,返回值为 5   () -> 5   // 2. 接收一个参数(数字类型),返回其2倍的值   x -> 2 * x   // 3. 接受2个参数(数字),并返回他们的差值   (x, y) -> x – y   // 4. 接收2个in
瑾诺学长
2018/09/21
8020
Lambda表达式
常见的语言中都提供Lambda语法糖,比如C#, Python, Golang等。本文将探讨下C++ 11引入的Lambda语法糖。语法糖是一种让程序员使用更加便利的一种语法,并不会带来额外的功能,比如Lambda,没有这种语法糖,其可以用已有的语法等价的实现出相应的功能。 有编程实践经验的同学一定能够快速的理解Lamdba产生的意义,而缺乏编程经验的同学,跟着我一起来梳理下Lamdba给我们带来了哪些便利性?
河边一枝柳
2021/10/14
5920
Lambda表达式
Lambda表达式本质:用作接口的实现(其实就是把之前实现接口的步骤简化了)。接口必须是函数式接口
十玖八柒
2022/08/01
2930
Lambda表达式
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,下面举例多线程的Runnable接口
晚上没宵夜
2020/03/10
3160
Lambda 表达式
Java Lambda 表达式是一种匿名函数;它是没有声明的方法,即没有访问修饰符、返回值声明和名字。
希希里之海
2019/09/10
4940
python lambda表达式 if_Python学习-lambda表达式
lambda只是一个表达式,函数体比def简单很多。lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。lambda表达式是起到一个函数速写的作用。允许在代码内嵌入一个函数的定义。
全栈程序员站长
2022/11/02
7610
python lambda表达式举例_Python中lambda表达式[通俗易懂]
lambda后面跟一个或多个参数,紧跟一个冒号,以后是一个表达式。冒号前是参数,冒号后是返回值。
全栈程序员站长
2022/11/07
6530
python lambda表达式详解_lambda python
lambda 表达式是现代编程语言争相引入的一种语法,如果说函数是命名的、方便复用的代码块,那么 lambda 表达式则是功能更灵活的代码块,它可以在程序中被传递和调用。
全栈程序员站长
2022/09/27
6650
Java Lambda表达式
在了解Lambda表达式之前我们先来区分一下面向对象的思想和函数式编程思想的区别 面向对象的思想: 做一件事情,找一个能解决这个事情的对象,调用他的方法来解决 函数时编程思想: 只要能获取到结果,谁去做的都不重要,重视的是结果,不重视过程 使用Lambda表达式的目的是为了简化我们的代码 匿名内部类虽然也简化了我们的代码,但是Lambda比他更简单,而且语法也更加少
一只胡说八道的猴子
2020/09/27
5470
Java    Lambda表达式
Python Lambda 表达式
Python 中 Lambda 表达式用于创建匿名函数 Lambda 表达式的作用是精简代码,但是不要为了精简而精简 >>> def ds(x): ... return(2*x + 1) >>> ds(10) 21 用 lambda 表达式重写后 >>> g = lambda x:(2*x + 1) >>> g(10) 21 多个参数的实现 >>> def add(x,y): ... return(x + y) >>> add(10,20) 30 使用 lambda 表达式 >>> g
tonglei0429
2019/07/19
3850

相似问题

在C ++ 11中什么是lambda的表达式?

1213

lambda表达式在-source 1.5中不受支持的问题?

1259

在lambda表达式中使用 does ()意味着什么?

2275

当使用 does ()在lambda表达式中意味着什么?

2277

付款哪里哪里?

1211
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文