Fastest way to sort 10 numbers? (numbers are 32 bit)
我正在解决一个问题,它涉及到快速排序10个数字(int32)。我的应用程序需要以百万倍的速度对10个数字进行排序。我正在对数十亿个元素的数据集进行抽样,每次我需要从中挑选10个数字(简化),然后对它们进行排序(并从排序后的10个元素列表中得出结论)。
目前我正在使用插入排序,但我想我可以为我的10个数字的特定问题实现一个非常快速的自定义排序算法,这将优于插入排序。
有人知道如何处理这个问题吗?
(根据HelloWorld的建议,对分类网络进行调查。)
29个比较/交换网络似乎是进行10个输入排序的最快方法。我使用了waksman在1969年发现的网络,在javascript中的这个例子,它应该直接翻译成C,因为它只是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | function sortNet10(data) { // ten-input sorting network by Waksman, 1969 var swap; if (data[0] > data[5]) { swap = data[0]; data[0] = data[5]; data[5] = swap; } if (data[1] > data[6]) { swap = data[1]; data[1] = data[6]; data[6] = swap; } if (data[2] > data[7]) { swap = data[2]; data[2] = data[7]; data[7] = swap; } if (data[3] > data[8]) { swap = data[3]; data[3] = data[8]; data[8] = swap; } if (data[4] > data[9]) { swap = data[4]; data[4] = data[9]; data[9] = swap; } if (data[0] > data[3]) { swap = data[0]; data[0] = data[3]; data[3] = swap; } if (data[5] > data[8]) { swap = data[5]; data[5] = data[8]; data[8] = swap; } if (data[1] > data[4]) { swap = data[1]; data[1] = data[4]; data[4] = swap; } if (data[6] > data[9]) { swap = data[6]; data[6] = data[9]; data[9] = swap; } if (data[0] > data[2]) { swap = data[0]; data[0] = data[2]; data[2] = swap; } if (data[3] > data[6]) { swap = data[3]; data[3] = data[6]; data[6] = swap; } if (data[7] > data[9]) { swap = data[7]; data[7] = data[9]; data[9] = swap; } if (data[0] > data[1]) { swap = data[0]; data[0] = data[1]; data[1] = swap; } if (data[2] > data[4]) { swap = data[2]; data[2] = data[4]; data[4] = swap; } if (data[5] > data[7]) { swap = data[5]; data[5] = data[7]; data[7] = swap; } if (data[8] > data[9]) { swap = data[8]; data[8] = data[9]; data[9] = swap; } if (data[1] > data[2]) { swap = data[1]; data[1] = data[2]; data[2] = swap; } if (data[3] > data[5]) { swap = data[3]; data[3] = data[5]; data[5] = swap; } if (data[4] > data[6]) { swap = data[4]; data[4] = data[6]; data[6] = swap; } if (data[7] > data[8]) { swap = data[7]; data[7] = data[8]; data[8] = swap; } if (data[1] > data[3]) { swap = data[1]; data[1] = data[3]; data[3] = swap; } if (data[4] > data[7]) { swap = data[4]; data[4] = data[7]; data[7] = swap; } if (data[2] > data[5]) { swap = data[2]; data[2] = data[5]; data[5] = swap; } if (data[6] > data[8]) { swap = data[6]; data[6] = data[8]; data[8] = swap; } if (data[2] > data[3]) { swap = data[2]; data[2] = data[3]; data[3] = swap; } if (data[4] > data[5]) { swap = data[4]; data[4] = data[5]; data[5] = swap; } if (data[6] > data[7]) { swap = data[6]; data[6] = data[7]; data[7] = swap; } if (data[3] > data[4]) { swap = data[3]; data[3] = data[4]; data[4] = swap; } if (data[5] > data[6]) { swap = data[5]; data[5] = data[6]; data[6] = swap; } return(data); } alert(sortNet10([5,7,1,8,4,3,6,9,2,0])); |
这里是网络的图形化表示,分为独立的阶段。。为了利用并行处理,可以将5-4-3-4-4-4-3-2分组改为4-4-4-4-3-2分组。氧化镁
当你处理这个固定的大小时,看看排序网络。这些算法有一个固定的运行时,并且独立于它们的输入。对于您的用例,您没有像某些排序算法那样的开销。
双音排序是这种网络的一种实现。这一个在CPU上最适合len(n)<=32。对于较大的输入,您可以考虑移动到GPU。https://en.wikipedia.org/wiki/sortingu网络
顺便说一句,比较排序算法的一个好页面是这里的这个(尽管它缺少
网址:http://www.sorting-algorithms.com
使用一个4组比较的排序网络,这样就可以在SIMD寄存器中进行比较。一对压缩的最小/最大指令实现一个压缩的比较器函数。抱歉,我现在没有时间去寻找我记得看到的关于这个的页面,但希望在simd或sse分类网络上搜索会发现一些东西。
X86SSE对于四个32位整数的向量有packed-32bit-integer最小和最大指令。avx2(haswell和更高版本)具有相同的特性,但对于256b矢量为8整数。还有有效的洗牌指令。
如果你有很多独立的小排序,可能可以使用向量并行进行4或8个排序。特别是,如果你随机选择元素(所以要排序的数据在内存中不会是连续的),你可以避免混乱,只需按你需要的顺序进行比较。10个寄存器保存来自4(avx2:8)的所有数据,10个整数的列表仍然保留6个regs作为临时空间。
如果您还需要对相关数据进行排序,那么向量排序网络的效率会降低。在这种情况下,最有效的方法似乎是使用打包比较来获取更改了哪些元素的掩码,并使用该掩码混合(引用)相关数据的向量。
一个展开的、无分支的选择排序怎么样?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | #include <iostream> #include #include <random> //return the index of the minimum element in array a int min(const int * const a) { int m = a[0]; int indx = 0; #define TEST(i) (m > a[i]) && (m = a[i], indx = i ); //see http://stackoverflow.com/a/7074042/2140449 TEST(1); TEST(2); TEST(3); TEST(4); TEST(5); TEST(6); TEST(7); TEST(8); TEST(9); #undef TEST return indx; } void sort( int * const a ){ int work[10]; int indx; #define GET(i) indx = min(a); work[i] = a[indx]; a[indx] = 2147483647; //get the minimum, copy it to work and set it at max_int in a GET(0); GET(1); GET(2); GET(3); GET(4); GET(5); GET(6); GET(7); GET(8); GET(9); #undef GET #define COPY(i) a[i] = work[i]; //copy back to a COPY(0); COPY(1); COPY(2); COPY(3); COPY(4); COPY(5); COPY(6); COPY(7); COPY(8); COPY(9); #undef COPY } int main() { //generating and printing a random array int a[10] = { 1,2,3,4,5,6,7,8,9,10 }; std::random_device rd; std::mt19937 g(rd()); std::shuffle( a, a+10, g); for (int i = 0; i < 10; i++) { std::cout << a[i] << ' '; } std::cout << std::endl; //sorting and printing again sort(a); for (int i = 0; i < 10; i++) { std::cout << a[i] << ' '; } return 0; } |
号
http://coliru.stacked-crooked.com/a/71e18bcc4f7fa18c6
唯一相关的行是前两个
它使用两个列表,并完全重新检查第一个列表十次,这将是一个糟糕的选择排序,但它避免分支和变长循环,这可能会补偿现代处理器和如此小的数据集。
基准我对排序网络进行基准测试,我的代码似乎较慢。不过,我还是试着把展开的和复制的东西拿出来。运行此代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | #include <iostream> #include #include <random> #include <chrono> int min(const int * const a, int i) { int m = a[i]; int indx = i++; for ( ; i<10; i++) //see http://stackoverflow.com/a/7074042/2140449 (m > a[i]) && (m = a[i], indx = i ); return indx; } void sort( int * const a ){ for (int i = 0; i<9; i++) std::swap(a[i], a[min(a,i)]); //search only forward } void sortNet10(int * const data) { // ten-input sorting network by Waksman, 1969 int swap; if (data[0] > data[5]) { swap = data[0]; data[0] = data[5]; data[5] = swap; } if (data[1] > data[6]) { swap = data[1]; data[1] = data[6]; data[6] = swap; } if (data[2] > data[7]) { swap = data[2]; data[2] = data[7]; data[7] = swap; } if (data[3] > data[8]) { swap = data[3]; data[3] = data[8]; data[8] = swap; } if (data[4] > data[9]) { swap = data[4]; data[4] = data[9]; data[9] = swap; } if (data[0] > data[3]) { swap = data[0]; data[0] = data[3]; data[3] = swap; } if (data[5] > data[8]) { swap = data[5]; data[5] = data[8]; data[8] = swap; } if (data[1] > data[4]) { swap = data[1]; data[1] = data[4]; data[4] = swap; } if (data[6] > data[9]) { swap = data[6]; data[6] = data[9]; data[9] = swap; } if (data[0] > data[2]) { swap = data[0]; data[0] = data[2]; data[2] = swap; } if (data[3] > data[6]) { swap = data[3]; data[3] = data[6]; data[6] = swap; } if (data[7] > data[9]) { swap = data[7]; data[7] = data[9]; data[9] = swap; } if (data[0] > data[1]) { swap = data[0]; data[0] = data[1]; data[1] = swap; } if (data[2] > data[4]) { swap = data[2]; data[2] = data[4]; data[4] = swap; } if (data[5] > data[7]) { swap = data[5]; data[5] = data[7]; data[7] = swap; } if (data[8] > data[9]) { swap = data[8]; data[8] = data[9]; data[9] = swap; } if (data[1] > data[2]) { swap = data[1]; data[1] = data[2]; data[2] = swap; } if (data[3] > data[5]) { swap = data[3]; data[3] = data[5]; data[5] = swap; } if (data[4] > data[6]) { swap = data[4]; data[4] = data[6]; data[6] = swap; } if (data[7] > data[8]) { swap = data[7]; data[7] = data[8]; data[8] = swap; } if (data[1] > data[3]) { swap = data[1]; data[1] = data[3]; data[3] = swap; } if (data[4] > data[7]) { swap = data[4]; data[4] = data[7]; data[7] = swap; } if (data[2] > data[5]) { swap = data[2]; data[2] = data[5]; data[5] = swap; } if (data[6] > data[8]) { swap = data[6]; data[6] = data[8]; data[8] = swap; } if (data[2] > data[3]) { swap = data[2]; data[2] = data[3]; data[3] = swap; } if (data[4] > data[5]) { swap = data[4]; data[4] = data[5]; data[5] = swap; } if (data[6] > data[7]) { swap = data[6]; data[6] = data[7]; data[7] = swap; } if (data[3] > data[4]) { swap = data[3]; data[3] = data[4]; data[4] = swap; } if (data[5] > data[6]) { swap = data[5]; data[5] = data[6]; data[6] = swap; } } std::chrono::duration<double> benchmark( void(*func)(int * const), const int seed ) { std::mt19937 g(seed); int a[10] = {10,11,12,13,14,15,16,17,18,19}; std::chrono::high_resolution_clock::time_point t1, t2; t1 = std::chrono::high_resolution_clock::now(); for (long i = 0; i < 1e7; i++) { std::shuffle( a, a+10, g); func(a); } t2 = std::chrono::high_resolution_clock::now(); return std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1); } int main() { std::random_device rd; for (int i = 0; i < 10; i++) { const int seed = rd(); std::cout <<"seed =" << seed << std::endl; std::cout <<"sortNet10:" << benchmark(sortNet10, seed).count() << std::endl; std::cout <<"sort: " << benchmark(sort, seed).count() << std::endl; } return 0; } |
与排序网络相比,无分支选择排序总是得到更好的结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | $ gcc -v gcc version 5.2.0 (GCC) $ g++ -std=c++11 -Ofast sort.cpp && ./a.out seed = -1727396418 sortNet10: 2.24137 sort: 2.21828 seed = 2003959850 sortNet10: 2.23914 sort: 2.21641 seed = 1994540383 sortNet10: 2.23782 sort: 2.21778 seed = 1258259982 sortNet10: 2.25199 sort: 2.21801 seed = 1821086932 sortNet10: 2.25535 sort: 2.2173 seed = 412262735 sortNet10: 2.24489 sort: 2.21776 seed = 1059795817 sortNet10: 2.29226 sort: 2.21777 seed = -188551272 sortNet10: 2.23803 sort: 2.22996 seed = 1043757247 sortNet10: 2.2503 sort: 2.23604 seed = -268332483 sortNet10: 2.24455 sort: 2.24304 |
。
问题并不是说这是一种基于Web的应用程序。引起我注意的一件事是:
I'm sampling a data set of billions of elements and every time I need to pick 10 numbers out of it (simplified) and sort them (and make conclusions from the sorted 10 element list).
号
作为一个软件和硬件工程师,这对我来说绝对是"FPGA"。我不知道你需要从一组排序的数字中得出什么样的结论,或者数据来自哪里,但我知道,在每秒处理1亿到10亿次这些"排序和分析"操作的过程中,这几乎是微不足道的。我以前做过FPGA辅助DNA测序工作。当问题非常适合于这种类型的解决方案时,几乎不可能击败FPGA的巨大处理能力。
在某种程度上,唯一的限制因素就是你能以多快的速度将数据输入到一个FPGA中,以及你能以多快的速度将数据取出。
作为参考,我设计了一个高性能的实时图像处理器,以每秒大约3亿像素的速度接收32位RGB图像数据。数据流通过FIR过滤器、矩阵乘法器、查找表、空间边缘检测块和许多其他操作流到另一端。所有这些都是在一个相对较小的XilinxVirtex2 FPGA上实现的,内部时钟的范围从33MHz到400MHz(如果我没记错的话)。哦,是的,它也有一个DDR2控制器实现,运行了两个DDR2内存库。
一个FPGA可以在每一个时钟转换上输出一个10个32位的数字,同时工作在数百兆赫。当数据填充处理管道时,操作开始时会有短暂的延迟。之后,您应该能够获得每个时钟一个结果。如果可以通过复制排序和分析管道来并行处理,则可以执行更多操作。原则上,解决方案几乎是微不足道的。
重点是:如果应用程序不是PC绑定的,并且数据流和处理与FPGA解决方案"兼容"(无论是独立的还是作为机器中的协处理器卡),那么无论采用何种算法,您都无法用任何语言编写的软件超越可达到的性能水平。
编辑:
只需快速搜索,找到一份可能对您有用的文件。它看起来可以追溯到2012年。你今天(甚至在那之前)的表现可以做得更好。这里是:
FPGA上的分类网络
我最近写了一个小类,它使用Bose Nelson算法在编译时生成一个排序网络。
它可以用来为10个数字创建一个非常快速的排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | /** * A Functor class to create a sort for fixed sized arrays/containers with a * compile time generated Bose-Nelson sorting network. * \tparam NumElements The number of elements in the array or container to sort. * \tparam T The element type. * \tparam Compare A comparator functor class that returns true if lhs < rhs. */ template <unsigned NumElements, class Compare = void> class StaticSort { template <class A, class C> struct Swap { template <class T> inline void s(T &v0, T &v1) { T t = Compare()(v0, v1) ? v0 : v1; // Min v1 = Compare()(v0, v1) ? v1 : v0; // Max v0 = t; } inline Swap(A &a, const int &i0, const int &i1) { s(a[i0], a[i1]); } }; template <class A> struct Swap <A, void> { template <class T> inline void s(T &v0, T &v1) { // Explicitly code out the Min and Max to nudge the compiler // to generate branchless code. T t = v0 < v1 ? v0 : v1; // Min v1 = v0 < v1 ? v1 : v0; // Max v0 = t; } inline Swap(A &a, const int &i0, const int &i1) { s(a[i0], a[i1]); } }; template <class A, class C, int I, int J, int X, int Y> struct PB { inline PB(A &a) { enum { L = X >> 1, M = (X & 1 ? Y : Y + 1) >> 1, IAddL = I + L, XSubL = X - L }; PB<A, C, I, J, L, M> p0(a); PB<A, C, IAddL, J + M, XSubL, Y - M> p1(a); PB<A, C, IAddL, J, XSubL, M> p2(a); } }; template <class A, class C, int I, int J> struct PB <A, C, I, J, 1, 1> { inline PB(A &a) { Swap<A, C> s(a, I - 1, J - 1); } }; template <class A, class C, int I, int J> struct PB <A, C, I, J, 1, 2> { inline PB(A &a) { Swap<A, C> s0(a, I - 1, J); Swap<A, C> s1(a, I - 1, J - 1); } }; template <class A, class C, int I, int J> struct PB <A, C, I, J, 2, 1> { inline PB(A &a) { Swap<A, C> s0(a, I - 1, J - 1); Swap<A, C> s1(a, I, J - 1); } }; template <class A, class C, int I, int M, bool Stop = false> struct PS { inline PS(A &a) { enum { L = M >> 1, IAddL = I + L, MSubL = M - L}; PS<A, C, I, L, (L <= 1)> ps0(a); PS<A, C, IAddL, MSubL, (MSubL <= 1)> ps1(a); PB<A, C, I, IAddL, L, MSubL> pb(a); } }; template <class A, class C, int I, int M> struct PS <A, C, I, M, true> { inline PS(A &a) {} }; public: /** * Sorts the array/container arr. * \param arr The array/container to be sorted. */ template <class Container> inline void operator() (Container &arr) const { PS<Container, Compare, 1, NumElements, (NumElements <= 1)> ps(arr); }; /** * Sorts the array arr. * \param arr The array to be sorted. */ template <class T> inline void operator() (T *arr) const { PS<T*, Compare, 1, NumElements, (NumElements <= 1)> ps(arr); }; }; #include <iostream> #include <vector> int main(int argc, const char * argv[]) { enum { NumValues = 10 }; // Arrays { int rands[NumValues]; for (int i = 0; i < NumValues; ++i) rands[i] = rand() % 100; std::cout <<"Before Sort: \t"; for (int i = 0; i < NumValues; ++i) std::cout << rands[i] <<""; std::cout <<" "; StaticSort<NumValues> staticSort; staticSort(rands); std::cout <<"After Sort: \t"; for (int i = 0; i < NumValues; ++i) std::cout << rands[i] <<""; std::cout <<" "; } std::cout <<" "; // STL Vector { std::vector<int> rands(NumValues); for (int i = 0; i < NumValues; ++i) rands[i] = rand() % 100; std::cout <<"Before Sort: \t"; for (int i = 0; i < NumValues; ++i) std::cout << rands[i] <<""; std::cout <<" "; StaticSort<NumValues> staticSort; staticSort(rands); std::cout <<"After Sort: \t"; for (int i = 0; i < NumValues; ++i) std::cout << rands[i] <<""; std::cout <<" "; } return 0; } |
注意,我们不使用
以下基准是用clang-o3编译的,并在我2012年年中的MacBookAir上运行。
随机数据排序将它与Dariop的代码进行比较,这里是对100万个大小为10的32位int数组进行排序所需的毫秒数:
硬编码分类网10:88.774 ms模板Bose Nelson Sort 10:27.815 ms
使用这种模板化方法,我们还可以在编译时为其他数量的元素生成排序网络。
对100万个不同大小的数组进行排序的时间(毫秒)。大小为2、4、8的数组的毫秒数分别为1.943、8.655、20.246。。
Glenn Teitelbaum负责展开的插入排序。
下面是6个元素的小数组的每种排序的平均时钟。基准代码和示例可以在以下问题中找到:最快的固定长度6 int数组排序
1 2 3 4 5 6 7 8 9 10 11 12 13 | Direct call to qsort library function : 326.81 Naive implementation (insertion sort) : 132.98 Insertion Sort (Daniel Stutzbach) : 104.04 Insertion Sort Unrolled : 99.64 Insertion Sort Unrolled (Glenn Teitelbaum) : 81.55 Rank Order : 44.01 Rank Order with registers : 42.40 Sorting Networks (Daniel Stutzbach) : 88.06 Sorting Networks (Paul R) : 31.64 Sorting Networks 12 with Fast Swap : 29.68 Sorting Networks 12 reordered Swap : 28.61 Reordered Sorting Network w/ fast swap : 24.63 Templated Sorting Network (this class) : 25.37 |
号
对于6个元素,它的执行速度与问题中最快的示例一样快。
排序数据的性能通常,输入数组可能已经排序或大部分排序。在这种情况下,插入排序是更好的选择。
氧化镁
您可能需要根据数据选择适当的排序算法。
用于基准测试的代码可以在这里找到。
虽然网络排序在小数组上具有很好的快速性,但如果适当优化,有时您无法击败插入排序。例如,使用2个元素进行批插入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | { final int a=in[0]<in[1]?in[0]:in[1]; final int b=in[0]<in[1]?in[1]:in[0]; in[0]=a; in[1]=b; } for(int x=2;x<10;x+=2) { final int a=in[x]<in[x+1]?in[x]:in[x+1]; final int b=in[x]<in[x+1]?in[x+1]:in[x]; int y= x-1; while(y>=0&&in[y]>b) { in[y+2]= in[y]; --y; } in[y+2]=b; while(y>=0&&in[y]>a) { in[y+1]= in[y]; --y; } in[y+1]=a; } |
您可以完全展开cx1[1]
为了使这更容易,可以使用递归
注意,要对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | template <class T, int NUM> class insert_sort; template <class T> class insert_sort<T,0> // stop template recursion // sorting 1 item is a no-op { public: static void place(T *x) {} static void sort(T * x) {} }; template <class T, int NUM> class insert_sort // use template recursion to do insertion sort // NUM is the index of the last item, eg. for x[10] call <9> { public: static void place(T *x) { T t1=x[NUM-1]; T t2=x[NUM]; if (t1 > t2) { x[NUM-1]=t2; x[NUM]=t1; insert_sort<T,NUM-1>::place(x); } } static void sort(T * x) { insert_sort<T,NUM-1>::sort(x); // sort everything before place(x); // put this item in } }; |
在我的测试中,这比排序网络示例更快。
由于与我在这里描述的类似的原因,当从这里获取排序网络时,以下排序功能(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | template<class IterType> inline void sort10_iterator(IterType it) { #define SORT2(x,y) {if(data##x>data##y)std::swap(data##x,data##y);} #define DD1(a) auto data##a=*(data+a); #define DD2(a,b) auto data##a=*(data+a), data##b=*(data+b); #define CB1(a) *(data+a)=data##a; #define CB2(a,b) *(data+a)=data##a;*(data+b)=data##b; DD2(1,4) SORT2(1,4) DD2(7,8) SORT2(7,8) DD2(2,3) SORT2(2,3) DD2(5,6) SORT2(5,6) DD2(0,9) SORT2(0,9) SORT2(2,5) SORT2(0,7) SORT2(8,9) SORT2(3,6) SORT2(4,9) SORT2(0,1) SORT2(0,2) CB1(0) SORT2(6,9) CB1(9) SORT2(3,5) SORT2(4,7) SORT2(1,8) SORT2(3,4) SORT2(5,8) SORT2(6,7) SORT2(1,2) SORT2(7,8) CB1(8) SORT2(1,3) CB1(1) SORT2(2,5) SORT2(4,6) SORT2(2,3) CB1(2) SORT2(6,7) CB1(7) SORT2(4,5) SORT2(3,4) CB2(3,4) SORT2(5,6) CB2(5,6) #undef CB1 #undef CB2 #undef DD1 #undef DD2 #undef SORT2 } |
为了调用这个函数,我给它传递了一个
插入排序平均需要29,6个比较,以便对10个输入进行排序,最佳情况为9,最差情况为45(给定输入的顺序相反)。
A 9,6,1 ShellSort平均需要25.5个比较来排序10个输入。最好的情况是14个比较,最差的是34个,排序一个反向输入需要22个。
因此,使用shellsort代替插入排序将平均大小写减少14%。尽管最佳情况增加了56%,但最坏情况减少了24%,这在检查最坏情况性能非常重要的应用程序中非常重要。相反的情况减少了51%。
由于您似乎熟悉插入排序,因此可以将算法实现为9,6的排序网络,然后在该网络之后附加插入排序(1):
1 2 3 4 5 6 7 8 | i[0] with i[9] // {9} i[0] with i[6] // {6} i[1] with i[7] // {6} i[2] with i[8] // {6} i[3] with i[9] // {6} i[0 ... 9] // insertion sort |
。