给定一个C ++向量(让我们说它是双倍的,让我们称之为unsorted
),创建一个包含{{1的排序副本的新向量sorted
的最高效方法是什么? }}?
考虑以下天真的解决方案:
unsorted
此解决方案包含两个步骤:
std::vector<double> sorted = unsorted;
std::sort(sorted.begin(), sorted.end());
。然而,在步骤1的初始副本中可能存在大量浪费的努力,特别是对于(例如)已经大部分排序的大型向量。
如果我手工编写这段代码,我可以将我的排序算法的第一遍与第1步结合起来,让第一遍读取unsorted
向量中的值,同时编写它们,根据需要进行部分排序,进入unsorted
。根据算法的不同,后续步骤可能只适用于sorted
中的数据。
有没有办法用C ++标准库,Boost或第三方跨平台库做这样的事情?
重要的一点是确保sorted
C ++向量的内存在排序开始之前不会被不必要地初始化为零。许多排序算法需要对sorted
向量进行即时随机写入访问,因此使用sorted
和reserve()
对第一次传递不起作用,但push_back()
会浪费时间初始化矢量。
编辑:由于答案和评论不一定明白为什么“天真的解决方案”效率低下,请考虑resize()
数组实际上已按排序顺序排列的情况(或者只是需要一个交换来排序)。在这种情况下,无论排序算法如何,对于天真的解决方案,每个值都需要至少读取两次 - 一次是复制,一次是排序。但是,通过复制 - 排序解决方案,读取次数可能会减半,因此性能大约翻了一番。当使用比unsorted
(可能是O(n)而不是O(n log n))更高性能的排序算法时,无论unsorted
中的数据如何,都会出现类似的情况。
答案 0 :(得分:4)
标准库 - 故意 - 没有复制时排序功能,因为副本是O(n)而std::sort
是O(n log n)。
因此,排序将完全支配任何更大的n值的成本。 (如果n很小,无论如何都不重要。)
答案 1 :(得分:1)
假设双精度矢量不包含NAN或无穷大等特殊数字,则双精度可以被视为64位符号+幅度整数,可以将其转换为最快的基数排序。这些“符号+幅度整数”需要转换为64位无符号整数。这些宏可用于转换来自符号+幅度的SM代表,无符号长整数(uint64_t)的ULL。假设为了使用这些宏而将双精度转换为unsigned long long类型:
#define SM2ULL(x) ((x)^(((~(x) >> 63)-1) | 0x8000000000000000ull))
#define ULL2SM(x) ((x)^((( (x) >> 63)-1) | 0x8000000000000000ull))
请注意,使用这些宏会将负零视为小于正零,但这通常不是问题。
由于基数排序需要初始读取传递来生成计数矩阵(然后将其转换为逻辑桶边界的起始或结束索引),那么在这种情况下,初始读取传递也将是一个复制传递生成计数矩阵。基数256种将使用大小为[8] [256]的矩阵,并且在复制之后,将执行8次基数排序。如果向量远大于高速缓存大小,则主导时间因子将是每个基数排序过程中的随机访问写入。