高性能堆排序

High performance heap sorting

本文关键字:堆排序 高性能      更新时间:2023-10-16

我有一个大小大于500万的向量,每次我想从向量中取出一个键值最小的元素,并对该元素进行一些处理。但是在处理这个特定元素时,vector中所有剩余的元素也会受到影响,从而使它们的键更新。所以下次如果我想从向量中取出键值最小的元素,我必须再对向量排序一次。问题是从向量中提取最小元素的次数将高达50万,因此程序运行速度很慢。为了让您更清楚地理解,我可以写下面的代码来说明:

void function(vector<MyObj*>& A)
{ //A.size() is near 5 million, maybe even more such as 50 million.
    make_heap(A.begin(), A.end(), compare); // compare function is self-defined.
    for (int i=0; i<500000; i++)
    {
        MyObj* smallest_elem = A.front();
        pop_heap(A.begin(), A.end());
        A.pop_back();
        Process_MyObj(smallest_elem); // here all of the elements 
                                      // in A will be affect, causing 
                                      // their keys changed.
        make_heap(A.begin(), A.end()); // Since all elements' keys in A changed,
                                       // so heap sorting A once again is 
                                       // necessary in my viewpoint.
    }
}

有没有办法使代码尽可能高效地运行?任何想法都是受欢迎的,不限于算法的改进,例如,并行或其他任何东西。非常感谢!

如果Process_MyObj确实影响了A中所有元素的键,我认为您可以做的不多。如果它只修改了一些键,则可以编写代码来更新堆中的单个元素。

正如你现在的代码,我不知道你从构建堆中获得了什么。我会做一个线性扫描来找到最小的元素,把它和最后一个元素交换,然后弹出最后一个元素。

您可以尝试对vector进行排序并按顺序选择元素,而不是使用堆。

它不会提高大0复杂度,但可能会提高常数因子

Process_MyObj中有多少时间,在堆操作中有多少时间——50/50%, 80/20% ?
这很重要,因为你想平衡两者。考虑以下一般设置:

Make a Todo list
Loop:
    work on items ...
    update the Todo list
花太多时间更新列表意味着没有足够的时间做真正的工作。首先测量进程/堆时间的比率。
要做到这一点,一个便宜的方法是用Process_MyObjcompare做两次,例如
 P + H = 1.0 sec
2P + H = 1.7 sec
=> P = .7, H = .3: P / H = 70 % / 30 %.


make_heap在线性时间内运行查看如何在最多进行3n次比较时实现stdmake-heap-所以加速会很困难。如果值是常量,则成堆的64位<32值,32索引>将比指针的缓存效率更高。

whats-new-in-purely-functional-data-structures-since-okasakicstheory。斯塔克列出了几十篇论文,大多是理论性的,但有一两个可能与你的问题有关。

真正的加速几乎总是针对特定的问题,而不是通用的。你能告诉我们更多关于真正的问题吗?


如果大多数pop都很小,而push很大,试着在大的排序列表前面放一个小的缓存堆。伪代码:

push:
    push( cacheheap )
pop:
    return min( cacheheap, bigsortedlist )

这可能是有效的如果 cacheheap留在真正的cpu缓存;ymmv。
(你可能会作弊,让bigsortedlist不准确,而不是每次都排序)