关于算法:对10个数字进行排序的最快方法?

Fastest way to sort 10 numbers? (numbers are 32 bit)

我正在解决一个问题,它涉及到快速排序10个数字(int32)。我的应用程序需要以百万倍的速度对10个数字进行排序。我正在对数十亿个元素的数据集进行抽样,每次我需要从中挑选10个数字(简化),然后对它们进行排序(并从排序后的10个元素列表中得出结论)。

目前我正在使用插入排序,但我想我可以为我的10个数字的特定问题实现一个非常快速的自定义排序算法,这将优于插入排序。

有人知道如何处理这个问题吗?


(根据HelloWorld的建议,对分类网络进行调查。)

29个比较/交换网络似乎是进行10个输入排序的最快方法。我使用了waksman在1969年发现的网络,在javascript中的这个例子,它应该直接翻译成C,因为它只是if语句、比较和交换的列表。

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]));

这里是网络的图形化表示,分为独立的阶段。10-input sorting network (Waksman, 1969)。为了利用并行处理,可以将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网络

顺便说一句,比较排序算法的一个好页面是这里的这个(尽管它缺少bitonic sort

网址: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

唯一相关的行是前两个#define

它使用两个列表,并完全重新检查第一个列表十次,这将是一个糟糕的选择排序,但它避免分支和变长循环,这可能会补偿现代处理器和如此小的数据集。

基准

我对排序网络进行基准测试,我的代码似乎较慢。不过,我还是试着把展开的和复制的东西拿出来。运行此代码:

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;
}

注意,我们不使用if (compare) swap语句,而是显式地为min和max编写三元运算符。这有助于推动编译器使用无分支代码。

基准

以下基准是用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。C++ Templated Bose-Nelson Static Sort timings

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]

为了使这更容易,可以使用递归template,而不需要函数开销。因为它已经是一个template,所以int也可以是一个template参数。这也使得除了10个以外的编码数组大小变得很容易创建。

注意,要对int x[10]进行排序,调用是insert_sort::sort(x);,因为类使用最后一项的索引。这可以被包装,但这将是更多的代码需要阅读。

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
    }
};

在我的测试中,这比排序网络示例更快。


由于与我在这里描述的类似的原因,当从这里获取排序网络时,以下排序功能(sort6_iterator()sort10_iterator_local()应该表现良好:

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
}

为了调用这个函数,我给它传递了一个std::vector迭代器。


插入排序平均需要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