Python set()是如何实现的?

Python set()是如何实现的?,python,data-structures,set,cpython,Python,Data Structures,Set,Cpython,我看到有人说python中的set对象具有O(1)成员身份检查。它们是如何在内部实现的?它使用什么样的数据结构?该实施还有什么其他影响 这里的每一个答案都很有启发性,但我只能接受一个,所以我将用最接近我最初问题的答案。谢谢大家的信息 根据: 事实上,CPython的集合被实现为类似于字典的东西 具有虚拟值(键是集合的成员),具有 利用这种价值缺失的优化 所以基本上,set使用哈希表作为其底层数据结构。这解释了O(1)成员资格检查,因为在哈希表中查找项平均是一个O(1)操作 如果你有这样的倾向,你

我看到有人说python中的
set
对象具有O(1)成员身份检查。它们是如何在内部实现的?它使用什么样的数据结构?该实施还有什么其他影响

这里的每一个答案都很有启发性,但我只能接受一个,所以我将用最接近我最初问题的答案。谢谢大家的信息

根据:

事实上,CPython的集合被实现为类似于字典的东西 具有虚拟值(键是集合的成员),具有 利用这种价值缺失的优化

所以基本上,
set
使用哈希表作为其底层数据结构。这解释了O(1)成员资格检查,因为在哈希表中查找项平均是一个O(1)操作


如果你有这样的倾向,你甚至可以浏览,根据,它主要是从
dict
实现中剪切粘贴而来的。

我认为这是一个常见的错误,
设置查找(或哈希表)不是O(1)。

在最简单的模型中,哈希函数是完全未指定的,并且表不会调整大小。对于哈希函数的最佳选择,具有开放寻址的大小为n的表没有冲突,最多可容纳n个元素,只有一次比较才能成功查找,而具有链接和k个键的大小为n的表具有最小的最大(0,k-n)冲突和O(1+k/n)查找比较。对于最差的哈希函数选择,每次插入都会导致冲突,哈希表会退化为线性搜索,每次插入都会进行Ω(k)摊销比较,成功查找最多会进行k次比较


相关:

我们都可以轻松访问,其中
set_lookkey()
前面的注释说明:

/*设置对象实现
由雷蒙德·D·赫廷格撰写和维护
派生自Lib/sets.py和Objects/dictobject.c。
所有操作使用的基本查找函数。
这是基于Knuth第3卷第2节中的算法D。6.4.
初始探测索引计算为哈希mod表大小。
随后的探测指数按照Objects/dictobject.c中的说明进行计算。
为了提高缓存的局部性,每个探测器都会检查一系列连续的
在移动到内存中其他位置的探测器之前,请先查看附近的条目。这只剩下
我们使用线性探测和开放寻址的混合。线性探测
减少哈希冲突的成本,因为连续的内存访问
通常比分散探头便宜得多。在线性测量步骤之后,
然后,我们对哈希值的高位使用开放寻址。这
有助于打破长长的碰撞链。
哈希上的所有算法都应忽略溢出。
与dictionary实现不同,lookkey函数可以返回
如果富比较返回错误,则为NULL。
*/
...
#ifndef线性探头
#定义线性探头9
#恩迪夫
/*这必须大于等于1*/
#定义扰动_移位5
静态设置项*
set_lookkey(PySetObject*so,PyObject*key,Py_hash\t hash)
{
...

当人们说集合有O(1)成员检查时,他们说的是平均值情况。在最坏的情况下(当所有散列值发生冲突时),成员检查是O(n)。请参阅

表示不调整大小的哈希表的时间复杂度为
O(1+k/n)
。此结果不直接应用于Python集,因为Python集使用调整大小的哈希表


Wikipedia的文章进一步指出,对于平均值情况,假设一个简单的统一哈希函数,时间复杂度是
O(1/(1-k/n))
,其中
k/n
可以用一个常数
c来限定,为了强调
set的
dict的
之间的区别,这里摘录了
setobject.c
注释部分,阐明了set与dict的主要区别

集合的用例与查找集合的词典有很大不同 关键点更可能出现。相反,集合主要是 关于成员资格测试,其中元素的存在在 因此,set实现需要针对这两个方面进行优化 已发现和未发现的案例


python中

集合的源代码在内部使用哈希表。让我们首先讨论一下哈希表。 假设有一些元素要存储在哈希表中,并且哈希表中有31个位置可以存储这些元素。让这些元素为:2.83、8.23、9.38、10.23、25.58、0.42、5.37、28.10、32.14、7.31。如果要使用哈希表,首先确定哈希表中存储这些元素的索引。模函数ion是确定这些索引的一种常用方法,因此让我们假设我们一次取一个元素,乘以100,然后应用模乘31。重要的是,对一个元素的每一个这样的操作都会产生一个唯一的数字,因为哈希表中的一个条目只能存储一个元素,除非允许链接。这样,每个元素都将存储在由通过模运算获得的索引所控制的一种位置。现在,如果您想在一个使用此哈希表存储元素的集合中搜索一个元素,您将在O(1)时间内获得该元素,因为该元素的索引是在恒定时间内使用模运算计算的。 为了解释模运算,我还要编写一些代码:

piles = [2.83, 8.23, 9.38, 10.23, 25.58, 0.42, 5.37, 28.10, 32.14, 7.31]

def hash_function(x):
    return int(x*100 % 31)

[hash_function(pile) for pile in piles]

输出:[4,17,8,0,16,11,10,20,21,18]

但它们确实需要固定的时间来查找项目:python-m timeit-s“s=set(范围(10))“5 in s”10000000循环,最好是3:0.0642 usec/循环python-m timeit-s”s=set(范围(10000000))“5 in s”10000000个循环,每个循环最好3:0.0634 usec…这是最大的不抛出的集合MemoryErrors@THC4k你所证明的是,查找X是正确的