C ++:对数字列表及其索引进行排序的最快方法

时间:2012-04-23 20:38:42

标签: c++ algorithm sorting quicksort

我有一个看似非常基本的问题,但是在“每个CPU滴答计数”的情况下(这是将在超级计算机上使用的更大算法的一部分)。

问题很简单:对无符号长long int数及其原始索引列表进行排序的最快方法是什么? (开头时,无符号long long int数字是完全随机的。)

Example :
Before
Numbers: 32 91 11 72
Indexes: 0 1 2 3
After
Numbers: 11 32 72 91
Indexes: 2 0 3 1 

以“最快的方式”,我的意思是:使用什么算法:std :: sort,C qsort,或网上提供的其他排序算法?使用什么容器(C数组,std :: vector,std :: map ...)?如何同时对索引进行排序(使用结构,std :: pair,std :: map ...)?

非常感谢!

编辑:要排序多少元素? - >典型的4Go数字

8 个答案:

答案 0 :(得分:16)

明显的出发点是为其定义operator<的结构:

struct data { 
    unsigned long long int number;
    size_t index;
};

struct by_number { 
    bool operator()(data const &left, data const &right) { 
        return left.number < right.number;
    }
};

...和一个用于保存数据的std :: vector:

 std::vector<data> items;

std::sort进行排序:

 std::sort(items.begin(), items.end(), by_number());

简单的事实是,普通容器(等等)足够高效,使用它们不会使您的代码效率大大降低。通过以不同的方式编写某些部分,可能能够做得更好,但您可能会更容易做得更糟。从可靠和可读开始,并测试 - 不要(尝试)过早优化。

编辑:当然在C ++ 11中,您可以改为使用lambda表达式:

std::sort(items.begin(), items.end(), 
          [](data const &a, data const &b) { return a.number < b.number; });

这通常更便于编写。可读性取决于 - 对于像这样简单的东西,我会说sort ... by_number非常易读,但这在很大程度上取决于您给比较运算符的名称。 lambda使实际的排序标准更容易找到,因此您无需仔细选择名称以使代码可读。

答案 1 :(得分:5)

理想情况下,

std::pairstd::sort符合您的要求:如果您将值放入pair.firstpair.second中的索引,则只需调用sort即可在pair s的向量上,像这样:

// This is your original data. It does not need to be in a vector
vector<long> orig;
orig.push_back(10);
orig.push_back(3);
orig.push_back(6);
orig.push_back(11);
orig.push_back(2);
orig.push_back(19);
orig.push_back(7);
// This is a vector of {value,index} pairs
vector<pair<long,size_t> > vp;
vp.reserve(orig.size());
for (size_t i = 0 ; i != orig.size() ; i++) {
    vp.push_back(make_pair(orig[i], i));
}
// Sorting will put lower values ahead of larger ones,
// resolving ties using the original index
sort(vp.begin(), vp.end());
for (size_t i = 0 ; i != vp.size() ; i++) {
    cout << vp[i].first << " " << vp[i].second << endl;
}

答案 2 :(得分:3)

事实证明,

std::sort比旧qsort更快,因为缺乏间接性和内联关键操作的可能性。

std::sort的实施可能会高度优化且难以击败,但并非不可能。如果您的数据长度固定且较短,则可能会发现Radix sort更快。 Timsort相对较新,为Python带来了良好的效果。

您可能会将索引数组与值数组分开,但我认为额外的间接级别将被证明是速度杀手。最好将它们放在一个结构或std::pair中。

与任何速度关键应用程序一样,您必须尝试一些实际的实现并进行比较,以确定哪个是最快的。

答案 3 :(得分:3)

可能值得分隔数字和索引然后只是排序索引,如下所示:

#include <vector>
#include <algorithm>
#include <iostream>

void PrintElements(const std::vector<unsigned long long>& numbers, const std::vector<size_t>& indexes) {

    std::cout << "\tNumbers:";
    for (auto i = indexes.begin(); i != indexes.end(); ++i)
        std::cout << '\t' << numbers[*i];
    std::cout << std::endl;

    std::cout << "\tIndexes:";
    for (auto i = indexes.begin(); i != indexes.end(); ++i)
        std::cout << '\t' << *i;
    std::cout << std::endl;

}

int main() {

    std::vector<unsigned long long> numbers;
    std::vector<size_t> indexes;

    numbers.reserve(4); // An overkill for this few elements, but important for billions.
    numbers.push_back(32);
    numbers.push_back(91);
    numbers.push_back(11);
    numbers.push_back(72);

    indexes.reserve(numbers.capacity());
    indexes.push_back(0);
    indexes.push_back(1);
    indexes.push_back(2);
    indexes.push_back(3);

    std::cout << "BEFORE:" << std::endl;
    PrintElements(numbers, indexes);

    std::sort(
        indexes.begin(),
        indexes.end(),
        [&numbers](size_t i1, size_t i2) {
            return numbers[i1] < numbers[i2];
        }
    );

    std::cout << "AFTER:" << std::endl;
    PrintElements(numbers, indexes);

    return EXIT_SUCCESS;

}

打印:

BEFORE:
        Numbers:        32      91      11      72
        Indexes:        0       1       2       3
AFTER:
        Numbers:        11      32      72      91
        Indexes:        2       0       3       1

这个想法是被排序的元素很小,因此在排序过程中可以快速移动。然而,在现代CPU上,间接访问numbers对缓存的影响可能会削弱这些收益,因此我建议在最终决定使用数据之前对实际数据量进行基准测试。

答案 4 :(得分:1)

使用std::vectorstd::sort。这应该提供最快的排序方法。要查找原始索引,请创建一个结构。

struct A {
    int num;
    int index;
}

然后使用自己的比较谓词进行排序,比较结构中的num。

struct Predicate {
    bool operator()(const A first, const A second) {
        return first.num < second.num;
    }
}

std::sort(vec.begin(), vec.end(), Predicate())

答案 5 :(得分:1)

struct SomeValue
{
    unsigned long long val;
    size_t index;
    bool operator<(const SomeValue& rhs)const
    { 
       return val < rhs.val;
    }
}

 #include <algorithm>
 std::vector<SomeValue> somevec;
 //fill it...
 std::sort(somevec.begin(),somevec.end());

答案 6 :(得分:1)

这将用于超级计算机吗?

在这种情况下,您可能需要研究并行排序算法。这对于对大型数据集进行排序才有意义,但如果您需要它,那么胜利是非常重要的。

答案 7 :(得分:0)

您可能会发现this是一个有趣的读物。我会从STL的那种开始,然后如果可能的话,尝试改进它。我不确定你是否可以在这台超级计算机上访问C ++ 11编译器(如gcc4.7),但我建议用std :: futures和std :: threads进行std :: sort会让你相当关于以可维护的方式并行化问题的方法。

这是another question,它将std :: sort与qsort进行比较。

最后,Dobb博士的this article比较了并行算法的性能。