Set 在检查值时实际上如何在内部工作?
How does Set actually work internal when checking for values?
这是一个基于计算机科学的非常普遍的问题,但根据有关它们如何工作的文献,这个问题似乎并不直观。这是一个与语言无关的问题,但与 Set 数据类型在内部的工作方式有关。
我用过很多次了,推荐使用来存储唯一值,快速访问。据推测,在 Big-O 表示法中,每次访问 Set 时其时间和复杂度为 O(1)。如果 Set 可能包含数千个项目,那怎么可能呢?即使项目是唯一的。
为了在 Set 中找到一个项目,它仍然必须扫描每个唯一的项目,这在 Big-O 中在时间和复杂度上都是 O(n)。我在这里遗漏了什么吗?
在此先感谢您的帮助!最彻底的答案获得赞成票!
A Set
是一种更普遍的对象,统称为 HashedCollections
。它们使用某种 HashTable
来实际存储和检索它们的元素。
给定任何 element
,这些 table 计算一个整数值,命名为 hash
。有几种众所周知的技术可以定义元素与其 hash
值之间的映射。有些是 固有的 ,因为 hash
不依赖于 element
的属性,后者可能会改变,因此 hash
在 element
的生命周期中保持不变。其他的是 外部的 ,因为它们可能取决于属性。但是,在后一种情况下,假定特定元素在从 HashedCollection
引用时不会被修改(否则 HashedCollection
必须是 rehashed
)。
存储element
的过程如下:
hash
是为 element
计算的。
- table 的
index
计算为 hash
对 table 的 length
取模的余数。
- 如果计算出的
index
处的插槽已被占用,则应用某些策略来解决 碰撞。
第 1 步应该非常快(例如,hash
没有 cryptographic
强度)。
步骤 2 假设(在大多数情况下)table 的长度是一个 prime 数(也使用 2
的幂)
步骤 3 基本上可以通过两种不同的方式解决:
- table 被顺序扫描
j
次,直到 index + j
的插槽空闲,或者
- 该元素被添加到在给定
index
(bucket) 碰撞的元素集合中
此外,如果没有足够的空槽(这增加了碰撞的可能性),table被放大并且rehashed
(因为modulo
改变了)。
如果有足够的空闲插槽和相当随机的索引机制分布,在 O(1)
中找到所需插槽的概率非常高。当然,如果有太多元素发生碰撞,平均复杂度就不再是 O(1)
,但这应该可以通过不断增长的策略 (+ rehash
).
来缓解
检索类似。要检查 element
是否属于集合,计算其 hash
和 modulo
并将 element
与目标槽的内容进行比较。如果比较失败,则搜索在桶中线性进行。
当没有 bucket
而增加了 indexes
时,删除元素会有些困难,但你明白了。
如果您真的想在工作中看到所有这些,请继续在任何 Smalltalk 方言中调试 HashedCollections
的基本操作。保证有很多乐趣。
这是一个基于计算机科学的非常普遍的问题,但根据有关它们如何工作的文献,这个问题似乎并不直观。这是一个与语言无关的问题,但与 Set 数据类型在内部的工作方式有关。
我用过很多次了,推荐使用来存储唯一值,快速访问。据推测,在 Big-O 表示法中,每次访问 Set 时其时间和复杂度为 O(1)。如果 Set 可能包含数千个项目,那怎么可能呢?即使项目是唯一的。
为了在 Set 中找到一个项目,它仍然必须扫描每个唯一的项目,这在 Big-O 中在时间和复杂度上都是 O(n)。我在这里遗漏了什么吗?
在此先感谢您的帮助!最彻底的答案获得赞成票!
A Set
是一种更普遍的对象,统称为 HashedCollections
。它们使用某种 HashTable
来实际存储和检索它们的元素。
给定任何 element
,这些 table 计算一个整数值,命名为 hash
。有几种众所周知的技术可以定义元素与其 hash
值之间的映射。有些是 固有的 ,因为 hash
不依赖于 element
的属性,后者可能会改变,因此 hash
在 element
的生命周期中保持不变。其他的是 外部的 ,因为它们可能取决于属性。但是,在后一种情况下,假定特定元素在从 HashedCollection
引用时不会被修改(否则 HashedCollection
必须是 rehashed
)。
存储element
的过程如下:
hash
是为element
计算的。- table 的
index
计算为hash
对 table 的length
取模的余数。 - 如果计算出的
index
处的插槽已被占用,则应用某些策略来解决 碰撞。
第 1 步应该非常快(例如,hash
没有 cryptographic
强度)。
步骤 2 假设(在大多数情况下)table 的长度是一个 prime 数(也使用 2
的幂)
步骤 3 基本上可以通过两种不同的方式解决:
- table 被顺序扫描
j
次,直到index + j
的插槽空闲,或者 - 该元素被添加到在给定
index
(bucket) 碰撞的元素集合中
此外,如果没有足够的空槽(这增加了碰撞的可能性),table被放大并且rehashed
(因为modulo
改变了)。
如果有足够的空闲插槽和相当随机的索引机制分布,在 O(1)
中找到所需插槽的概率非常高。当然,如果有太多元素发生碰撞,平均复杂度就不再是 O(1)
,但这应该可以通过不断增长的策略 (+ rehash
).
检索类似。要检查 element
是否属于集合,计算其 hash
和 modulo
并将 element
与目标槽的内容进行比较。如果比较失败,则搜索在桶中线性进行。
当没有 bucket
而增加了 indexes
时,删除元素会有些困难,但你明白了。
如果您真的想在工作中看到所有这些,请继续在任何 Smalltalk 方言中调试 HashedCollections
的基本操作。保证有很多乐趣。