【发布时间】:2012-05-11 12:56:43
【问题描述】:
我目前正在处理一个遇到性能问题的嵌入式设备项目。分析找到了一个我想消除的 O(N) 操作。
我基本上有两个数组int A[N] 和short B[N]。 A 中的条目是唯一的,并按外部约束排序。最常见的操作是检查特定值a 是否出现在A[] 中。不太常见但仍然常见的是对A[] 元素的更改。新值与之前的值无关。
由于最常见的操作是查找,这就是B[] 的用武之地。它是A[] 中索引的排序数组,因此A[B[i]] < A[B[j]] 当且仅当i<j。这意味着我可以使用二进制搜索在A 中找到值。
当然,当我更新A[k]时,我必须在B中找到k并将其移动到一个新的位置,以保持搜索顺序。因为我知道A[k] 的旧值和新值,所以这只是B[] 的子集memmove() 在k 的旧位置和新位置之间的一个memmove()。这是我需要修复的 O(N) 操作;因为A[k] 的新旧值基本上是随机的,所以我平均移动 N/2 N/3 个元素。
我使用[](int i, int j) { return A[i] < A[j]; } 作为谓词查看了std::make_heap。在这种情况下,我可以轻松地将B[0] 指向A 的最小元素,并且更新B 现在是一个廉价的O(log N) 重新平衡操作。但是,我通常不需要 A 的最小值,我需要查找是否存在任何给定值。现在在B 中进行 O(N log N) 搜索。 (我的 N 个元素中有一半位于堆深度 log N,四分之一位于 (log N)-1 等),这与直接在 A 中的愚蠢 O(N) 搜索相比没有任何改进。
考虑到std::set 有 O(log N) 的插入和查找,我想说应该可以在此处获得相同的更新和查找性能。但是我该怎么做呢? B 需要另外订购吗?不同的类型?
B 当前是short [N],因为A 和B 加起来大约是我的CPU 缓存大小,而我的主内存要慢很多。从 6*N 到 8*N 字节不是很好,但如果我的查找和更新都达到 O(log N) 仍然可以接受。
【问题讨论】:
-
确切地说:移动2个随机元素平均需要移动N/3个元素,而不是N/2个(第一个元素之前和第二个之后的元素不需要移动)。不过,这是一个非常有趣的问题!
-
也许我不明白,但我认为您可以保持数组排序并进行二进制搜索。当您需要排序时,这将花费 nlogn 并在您需要搜索时记录 n。
-
N 大概有多大?另外,为什么包含搜索 O(N log N) 而不仅仅是 O(N)?在堆中搜索也只是 O(N)(你甚至不必按堆顺序)
-
@memo:这就是为什么我说
A的顺序是外部强加的。我使用B创建了我可以控制的第二个订单。但是,即使我可以重新排序A而不是B,它仍然是一个 O(N) 操作(在A[old]和A[new]之间向上或向下移动一个位置)。我的目标是 O(log N)。
标签: c++ algorithm complexity-theory