数据结构为O(log N)查找和更新,考虑到小一级缓存(Data structure for O(l

2019-07-29 08:58发布

我目前工作的地方我遇到性能问题的嵌入式设备项目。 谱已经位于我想消除的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] ,我必须要找到kB并将其移动到新位置,以维持搜索顺序。 由于我知道的新旧值A[k] ,这只是一个memmove()的一个子集的B[]的新老位置之间k 。 这是O(N)的操作,我需要修复; 自的新旧值A[k]基本上是随机的我上移动平均约N / 2 N / 3个元素。

我看着std::make_heap使用[](int i, int j) { return A[i] < A[j]; } [](int i, int j) { return A[i] < A[j]; }作为谓词。 在这种情况下,我可以很容易地使B[0]指向的最小元素A ,以及更新B现在是一个便宜的为O(log N)重新平衡操作。 不过,我一般不需要的最小值,我需要找到,如果任何给定的值存在。 这就是现在O(N日志N)在搜索B 。 (我的N个元素的一半是在堆深度日志N,在(日志N)-1四分之一等),这是在哑O(N)直接在搜索没有改善A

考虑到std::set有O(日志N)的插入和查找,我会说,它应该可以在这里得到相同的性能更新和查找。 但我怎么做呢? 我是否需要另一份订单B ? 不同的类型?

B目前是short [N]因为AB一起是我的CPU高速缓存的大小,和我的主存储器是慢了很多。 从6要去* N 8 * N个字节就不会很好,但如果我发现还是可以接受的,并更新到O(日志N)两种。

Answer 1:

如果操作只有(1)检查是否值“a”在A属于A和(2)更新值,你为什么不到位数组排序B的使用哈希表 ? 特别是如果不增长或尺寸的缩小和值只改变,这将是一个更好的解决方案。 哈希表不需要比阵列显著更多的存储器。 (另外,B应不改成一个堆,但到二叉搜索树,这可能是自平衡,例如伸展树或红黑树,但树需要左和右的,因为额外的内存指针。)

一种生长存储器使用从6N至8n字节的实际的解决方案是瞄准准确50%填充哈希表,即,使用一个由2N短裤的阵列的哈希表。 我会建议实施杜鹃散列机制(见http://en.wikipedia.org/wiki/Cuckoo_hashing )。 阅读文章进一步,你会发现,你可以通过使用多个散列函数获得50%以上的负载系数(即推内存消耗从8N下来,对,比方说,7N)。 “ 利用短短三年散列函数的负载增加至91%。”

维基百科:

通过Zukowski向等人的研究。 已经表明,杜鹃散列比链式散列快得多对现代处理器小,高速缓存驻留哈希表 。 肯尼斯罗斯所示杜鹃散列bucketized版本(即使用包含多于一个的键水桶变体)比常规方法也可用于大的哈希表的速度更快,当空间利用率是高的。 所述bucketized杜鹃哈希表的性能通过Askitis进一步调查,其性能对备选哈希方案相比。



Answer 2:

std::set通常提供O(日志(n))的插入和使用二叉搜索树删除。 这不幸的是采用3 * N的空间对于大多数基于指针的实现。 假设字长度的数据,1用于数据,2指针左和每个节点上右子。

如果你有一些常数N和可以保证ceil(log2(N))是不到一半的字的大小可以使用树节点每2 * N大小的固定长度的数组。 使用1数据,1为两个子节点的索引,存储作为字的上半部和下半部。 这是否让你使用一个自我平衡的二叉搜索树的一些方式取决于你的N和字的大小。 对于16位系统,你只得到N = 256,但它的32 65K。



Answer 3:

既然你有有限的N,你不能使用std::set<short, cmp, pool_allocator> B与Boost的pool_allocator



文章来源: Data structure for O(log N) find and update, considering small L1 cache