【发布时间】:2019-05-22 16:39:25
【问题描述】:
我需要快速找出两个给定集合中有多少整数。这些集合只被写入一次,但这个操作将使用不同的集合对执行多次。这些集合包含 5-30 个整数,其中最大的整数是 840000。
我最初尝试迭代一个 Vec 并为每个元素检查它是否存在于另一个 Vec 中。然后我决定改用BTreeSet,因为它在检查集合中是否存在整数时应该会更快,但似乎并非如此。 Vec 在稳定的 Rust 1.34 下以发布模式调用数千个集合时需要约 72 毫秒和 BTreeSet 约 96 毫秒,在夜间使用时具有相同的性能。
这是Vec 实现:
use std::cmp;
fn main() {
let mut sets = Vec::with_capacity(1000);
for i in 1..1000 {
let mut set = Vec::new();
for j in 1..i % 30 {
set.push(i * j % 50000);
}
sets.push(set);
}
for left_set in sets.iter() {
for right_set in sets.iter() {
calculate_waste(left_set, right_set);
}
}
}
fn calculate_waste(left_nums: &Vec<usize>, right_nums: &Vec<usize>) -> usize {
let common_nums = left_nums.iter().fold(0, |intersection_count, num| {
intersection_count + right_nums.contains(num) as usize
});
let left_side = left_nums.len() - common_nums;
let right_side = right_nums.len() - common_nums;
let score = cmp::min(common_nums, cmp::min(left_side, right_side));
left_side - score + right_side - score + common_nums - score
}
这是BTreeSet 的实现:
use std::cmp;
use std::collections::BTreeSet;
fn main() {
let mut sets = Vec::with_capacity(1000);
for i in 1..1000 {
let mut set = BTreeSet::new();
for j in 1..i % 30 {
set.insert(i * j % 50000);
}
sets.push(set);
}
for left_set in sets.iter() {
for right_set in sets.iter() {
calculate_waste(left_set, right_set);
}
}
}
fn calculate_waste(left_nums: &BTreeSet<usize>, right_nums: &BTreeSet<usize>) -> usize {
let common_nums = left_nums.intersection(&right_nums).count();
let left_side = left_nums.len() - common_nums;
let right_side = right_nums.len() - common_nums;
let score = cmp::min(common_nums, cmp::min(left_side, right_side));
left_side - score + right_side - score + common_nums - score
}
它是使用命令运行的(-w 50 使其忽略前 50 次运行):
hyperfine "cargo run --release" -w 50 -m 100
程序的完整代码可用here。
BTreeSet 实现是否较慢,因为集合中的整数太少而无法使其 O(log n) 访问时间发光?如果是这样,我还能做些什么来加快这个功能?
【问题讨论】:
-
对于超出此问题的问题,您可能需要考虑使用我希望能够击败两者的性能的位集。
-
我认为您的集合确实太小,无法从
BTreeSet获得任何东西。 Rust 实现中的“B”是 6,因此单个节点中最多存储 11 个数字。每个节点都是线性扫描的,因此几乎没有机会在向量上获得任何东西。无论集合大小如何,对已排序向量的并行扫描总是会击败BTreeSet,因为它是O(m + n),其中m和n是两个向量的大小。 -
Benchmarking the core algorithm via Criterion.rs 产生
vec time: [61.092 ms 61.882 ms 62.686 ms]和set time: [91.517 ms 92.385 ms 93.441 ms],因此显然创建开销不应单独归咎于。
标签: vector rust b-tree set-intersection