【发布时间】:2017-11-07 08:51:58
【问题描述】:
假设我有一组通用的索引对象 U 和这些对象的子集 S。 S 很大(例如,1,000,000 个元素),但 U 更大(例如,至少 100,000,000 个)。
我想对这些集合执行两个基本操作:
(1) 给定任意整数x,从0到U的大小减1,检查S的成员关系,如果不是成员,则将x添加到S,然后
(2) 从S 中选择(并删除)一个随机元素。
为了执行操作 (1) 的第一部分,对我来说保留一个大小为 U 的布尔向量 v 是有意义的,如果元素 x 是集合S的成员。
但是,因为U 比S 大得多,所以在v 中选择一个随机元素并希望它也是S 中的一个元素是没有意义的。如果U 比S 大100 倍,那么它只会找到S 的元素,平均每100 次尝试一次。
因此,为了执行第二个操作,维护S 中元素的索引列表并从中选择一个随机元素是有意义的。
现在唯一的问题是,现在有两个相同数据的副本,并且每次操作都需要分别更新它们。这是第一个操作的伪代码:
** operation 1 - check membership and add **
input: boolean vector, v
integer vector, S
integer, x
if v[x] is not true:
v[x] = true
append x to S
return
这相对简单,但它必须更新索引向量,即使它没有使用它。这是第二个操作:
** operation 2 - select and remove random element of S **
input: boolean vector, v
integer vector, S
generate random integer x between 0 and size of S
set v[S[x]] to false
remove S[x] from S
return
维护数据的两个副本使这两个操作变得更加复杂,因为每个都必须更新两个数据结构,即使它只需要一个。这是不好的做法吗?
我能想到的唯一选择是使用其中一个。但这使一种操作更简单,而另一种则更复杂。例如(只给出比较复杂的):
** operation 1 - check membership and add**
input: integer vector, S
integer, x
iterate over S
if x in S:
return
else:
append x to S
return
所以每次都必须遍历整个S,而不是单次查找,并且
** operation 2 - select and remove random element of S **
input: boolean vector, v
while true:
generate random integer x between 0 and size of S
if v[x] true:
v[x] = false
return
这两种方法似乎都非常低效,特别是如果U 和S 的大小很大,并且U 和S 之间的差异也很大。有没有一种方法可以只用一个数据结构有效地执行这两项操作?或者维护同一事物的两个副本真的没有什么大问题吗?
编辑:
我正在编写的代码是用 c++ 编写的,所以我想我是在特别询问 c++ 数据结构,但这个问题并不是特定于语言的。
【问题讨论】:
-
std::set<int>应该能够在log N中执行任一操作 -
啊!这真是个好消息。我原以为那套东西太麻烦了。如果我还必须在不插入或删除任何内容的情况下进行大量成员资格查找,
std::set<int>仍然可以吗?或者我会更好地为查找维护一个布尔向量?O(log N)和O(1)之间的区别对于大型N来说是相当大的! -
按照 Bjarns S. 的说法,一个类是“拥有不变量”的东西。诚然,您不想最小化范围,但这实际上是一种设计原则,以保持数据之间的一致性,根据它们自己的类型,这些数据可能具有无效值。这是封装。
-
您需要在内存和时间复杂度之间进行权衡。
标签: c++ performance data-structures