Vectorlist vl的长度为100000000,最大值。 101个不同的整数值。什么是最好和最快的排序算法?
我尝试了计数排序,(桶排序),......,但它们还不够快。每个整数(+ - )都有效。 100000000,101个不同的整数是随机生成的。感谢您的回复!我最好的算法大约是0.620秒。
答案 0 :(得分:1)
使用unorder_set
查找唯一值,然后将这些唯一值放入vector
并对其进行排序;然后将原件放入unorder_multiset
以计算值,例如:
vector<int> v;
// fill v with values
unordered_set<int> s(begin(v), end(v));
vector<int> sorted_v(begin(s), end(s));
sort(begin(sorted_v), end(sorted_v));
unordered_multiset<int> v_count(begin(v), end(v));
for (size_t i = 0; i < sorted_v.size(); ++i)
cout << "For the " << i << "th value == " << sorted_v[i] << " there are " << v_count.count(v[i]) << " of them." << endl;
答案 1 :(得分:1)
根据wiki(参见算法比较表),我们应该使用计数排序,因为我们没有太多不同的值。
首先,我认为我们的值为0-100,并使用以下代码:
void sort(std::vector<int>& v)
{
double start = std::clock();
int* table = new int[MAX];
for (int i = 0; i < MAX; ++i)
{
table[i] = 0;
}
for (int i = 0; i < size; ++i)
{
++table[v[i]];
}
int cur = 0;
for (int i = 0; i < MAX; ++i)
{
for (int j = 0; j < table[i]; ++j)
{
v[cur++] = i;
}
}
delete[] table;
std::cout << "count sort over char array took " << (std::clock() - start) / CLOCKS_PER_SEC << " s" << std::endl;
}
此代码在我的计算机上0.149s
与3.002s
使用的std::sort
进行了对比。
这是计数排序的经典实现,但现在尝试加速它,删除一些过多的计算:
void sort6(int* v, int size)
{
double start = std::clock();
int* table = new int[MAX];
for (int i = 0; i < MAX; ++i)
{
table[i] = 0;
}
int* end = v + size;
for (int* vi = v; vi < end; ++vi)
{
++table[*vi];
}
int* cur = v;
for (int i = 0; i < MAX; ++i)
{
int count = table[i];
for (int j = 0; j < count; ++j)
{
*(cur++) = i;
}
}
std::cout << "count sort with pointers over char array took " << (std::clock() - start) / CLOCKS_PER_SEC << " s" << std::endl;
delete[] v;
delete[] table;
}
这提供了约0.076s
。
其次,假设我们的值不是0-100,我使用以下算法:
不幸的是,目前我没有时间实施并检查,但我确定答案就在那里。
答案 2 :(得分:1)
以上是上述其他一些用户描述的算法的完整实现。 总算法复杂度为O(n)。
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <cstdint>
void special_sort(std::vector<int>& v, const size_t nExpectedMaxDifferentValues)
{
typedef int_fast32_t Value;
typedef size_t Count;
static_assert(sizeof(Value) >= sizeof(int), "please define Value to int on this platform");
struct ValHash
{
inline std::size_t operator()(const Value k) const
{
return k;
}
};
std::unordered_map<Value, Count, ValHash> counts;
counts.reserve(nExpectedMaxDifferentValues * 100);
for (const auto x : v)
++counts[x];
std::vector<Value> sorted_numbers;
sorted_numbers.reserve(counts.size());
for (const auto& p : counts)
sorted_numbers.push_back(p.first);
std::sort(std::begin(sorted_numbers), std::end(sorted_numbers));
// fill vector with sorted data:
int* p = v.data();
for (const auto x : sorted_numbers)
{
for (Count i = counts[x]; i > 0; --i)
{
*p++ = x;
}
}
}
测试速度的主要功能:
#include <random>
#include <limits>
#include <time.h>
#include <iostream>
int main()
{
std::cout << "Initialize..." << std::endl;
const size_t N = 100000000;
const size_t M = 101;
std::mt19937 gen(5); // use constant to easily reproduce the test
std::uniform_int_distribution<int> disInt(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
std::vector<int> v1;
v1.reserve(M);
for (size_t i = 0; i < M; ++i)
v1.push_back(disInt(gen));
std::uniform_int_distribution<size_t> disIndex(0, M-1);
std::vector<int> v2;
v2.reserve(N);
for (size_t i = 0; i < N; ++i)
v2.push_back(v1[disIndex(gen)]);
std::cout << "Sort..." << std::endl;
const clock_t begin_time = clock();
special_sort(v2, M);
const double seconds = double(clock() - begin_time) / CLOCKS_PER_SEC;
std::cout << "Sorting took " << int(seconds * 1000) << " ms" << std::endl;
return 0;
}
我笔记本的程序输出(由MSVC 2013 Update 5编译为 x86_64 ,在Core i7-4700MQ CPU @ 2.40GHz上运行):
Initialize...
Sort...
Sorting took 374 ms
有许多魔法和半魔法优化可以获得这个结果:
答案 3 :(得分:1)
除了Sergey answer之外,您还可以使用多个线程并行运行计数,这至少可以使该过程加速2次。
所以而不是:
std::unordered_map<int, size_t> counts;
counts.reserve(nExpectedMaxDifferentValues * 100);
for (const auto x : v)
++counts[x];
我们可以生成多个线程,这些线程都可以完成部分工作(仅使用Windows线程进行演示):
// Spawn 8 threads and spread the work
const int numberOfThreads = 8;
PartialResult partialResults[numberOfThreads];
HANDLE threadHandles[numberOfThreads];
const size_t partialSize = v.size() / numberOfThreads;
std::vector<int>::iterator it = v.begin();
for (auto i = 0; i < numberOfThreads; i++)
{
partialResults[i].reserve = nExpectedMaxDifferentValues * 100;
partialResults[i].begin = it;
it += partialSize;
partialResults[i].end = (i == numberOfThreads - 1) ? v.end() : it;
threadHandles[i] = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)partial_count, (LPVOID)&partialResults[i], 0, NULL);
}
// Wait for all threads to finish
::WaitForMultipleObjects(numberOfThreads, threadHandles, TRUE, INFINITE);
for (auto i = 0; i < numberOfThreads; i++)
::CloseHandle(threadHandles[i]);
// Aggregate counts (this could also be done in parallel)
std::unordered_map<int, size_t> counts;
counts.reserve(nExpectedMaxDifferentValues * 100);
for (auto i = 0; i < numberOfThreads; i++)
for (const auto x : partialResults[i].counts)
counts[x.first] += x.second;
PartialResult
和partial_count
的位置:
struct PartialResult {
std::unordered_map<int, size_t> counts;
std::vector<int>::iterator begin;
std::vector<int>::iterator end;
size_t reserve;
};
DWORD WINAPI partial_count(_In_ LPVOID lpParameter)
{
auto partialResult = (PartialResult*)lpParameter;
partialResult->counts.reserve(partialResult->reserve);
for (auto it = partialResult->begin; it < partialResult->end; it++)
++partialResult->counts[*it];
return 0;
}
上面的代码导致我的设置执行时间为390毫秒而不是860毫秒,并且可以通过并行聚合部分计数来改进。
答案 4 :(得分:0)
我认为,对于这项任务,计算排序是最合适的。 但是,如果您只需要尽快打印排序的整数列表,请考虑不要保存整数 - 但只保留地图中的计数器。