我有一个看似非常基本的问题,但是在“每个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数字答案 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::pair
和std::sort
符合您的要求:如果您将值放入pair.first
和pair.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::vector
和std::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比较了并行算法的性能。