我的算法需要通过删除一个元素来迭代地收缩一个集合,并在每次迭代中对删除的元素和收缩的集合做一些事情。
- 我需要一个真正的快速查找集,而不仅仅是一个包含唯一元素的向量。
- 元素的选择是任意的:算法的结果并不取决于访问的顺序,性能可能会随着选择的不同而有很大的变化,但是假设我想要最简单的代码,让集合自己选择它可以有效删除的元素。
- 克隆元素的成本很低(实际上很可能是可复制的整数,但让我们在这里的示例中明确地说明克隆)。
顺便说一下,算法是the basic form of the Bron–Kerbosch algorithm。该算法的更智能版本工作更快(大多数情况下),因为它们不会让元素的选择是任意的,我想知道与优化弹出操作相比,这种努力的回报是多少。
Python集合中有一个pop
成员,它可以很好地完成这个任务,在Scala和Go语言中,选择并删除散列集合中的“first”元素似乎可以很好地完成这个任务(其中“first”对应于迭代器),在Rust语言中,它类似于:
// split off an arbitrary element from a (non-empty) set
pub fn pop<T>(set: &mut HashSet<T>) -> T
where
T: Eq + Clone + std::hash::Hash,
{
let elt = set.iter().next().cloned().unwrap();
set.remove(&elt);
elt
}
与其他语言相比,这似乎是一个性能瓶颈,但即使是以一种看似幼稚的方式在Rust中进行这种迭代:复制序列,然后弹出sequence. I benchmarked some implementations of a pop-like function on the playground中的元素,但与原始方法相比,没有一种方法的性能更好。
一开始,我以为删除一个元素并不昂贵,但用iter().next()
选择一个元素代价很高,但仔细检查后发现并非如此,至少与其他语言相比是这样()。
可以理解,使用retain
并没有什么帮助:它总是迭代整个集合,还有别的选择吗
()仔细检查一下,iter().next()
非常便宜,因为微基准测试是可信的。Separate microbenchmarks说从一组元素中选择任意元素的成本(在我的系统上以纳秒为单位):
| Type of set | Number of elements in set instance
| | 100 | 10,000 | 1,000,000
| Rust HashSet | 2 | 2 | 2
| Rust BTreeSet | 11 | 12 | 13
| Go map[]struct{} | 27 | 31 | 94
| Python set | 125 | 125 | 125
3条答案
按热度按时间tag5nh1u1#
我使用的集合是整数
不要使用
HashSet
;BTreeSet
具有更好和更一致的性能。对于
N
= 100000 ...wecizke32#
我想同样的建议也适用于Can I randomly sample from a HashSet efficiently?:将集合复制为向量,以便对其进行迭代,如"sequenced" solution in the benchmark:
这意味着,如果您只需要收缩集合一次或几次(选择任意元素),或者如果集合的内容不能被廉价地克隆,那么这个答案就不适用。
nfg76nw03#
您的代码可以稍微简化:
如果你想删除
HashSet
中的所有元素,那么你应该使用drain
迭代器--它非常高效。Rust标准库中的
HashSet
速度不够快。请尝试用hashbrown机箱中的一个替换它。