接着上一回的说,对STL几种排序算法做一比较,比较并不全面,仅仅对std::sort()、std::stable_sort()、C标准库qort和std::heap_sort()做一比较,因为这是用的最多的,其底层实现已足够说明日常生活中排序问题所需要考虑的问题。
程序代码如下:
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 |
// 本文件实现了一个随机数发生器,其速度和随机特性均远远优于标准库,感谢Chipset的指点。 #ifndef mtrandom_HPP_ #define mtrandom_HPP_
#include <stddef.h>
class mtrandom { public: mtrandom() : left(1) { init(); } explicit mtrandom(size_t seed) : left(1) { init(seed); } mtrandom(size_t* init_key, int key_length) : left(1) { int i = 1, j = 0; int k = N > key_length ? N : key_length; init(); for(; k; --k) { state[i] = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> 30)) * 1664525UL))+ init_key[j] + j; // non linear state[i] &= 4294967295UL; // for WORDSIZE > 32 machines ++i; ++j; if(i >= N) { state[0] = state[N - 1]; i = 1; } if(j >= key_length) j = 0; } for(k = N - 1; k; --k) { state[i] = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> 30)) * 1566083941UL)) - i; // non linear state[i] &= 4294967295UL; // for WORDSIZE > 32 machines ++i; if(i >= N) { state[0] = state[N - 1]; i = 1; } } state[0] = 2147483648UL; // MSB is 1; assuring non-zero initial array }
void reset(size_t rs) { init(rs); next_state(); }
size_t rand() { size_t y; if(0 == --left) next_state(); y = *next++; // Tempering y ^= (y >> 11); y ^= (y << 7) & 0x9d2c5680UL; y ^= (y << 15) & 0xefc60000UL; y ^= (y >> 18); return y; }
double real() { return (double)rand() / -1UL; }
// generates a random number on [0,1) with 53-bit resolution double res53() { size_t a = rand() >> 5, b = rand() >> 6; return (a * 67108864.0 + b) / 9007199254740992.0; }
private: void init(size_t seed = 19650218UL) { state[0] = seed & 4294967295UL; for(int j = 1; j < N; ++j) { state[j] = (1812433253UL * (state[j - 1] ^ (state[j - 1] >> 30)) + j); // See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. // In the previous versions, MSBs of the seed affect // only MSBs of the array state[]. // 2002/01/09 modified by Makoto Matsumoto state[j] &= 4294967295UL; // for >32 bit machines } }
void next_state() { size_t* p = state; int i; for(i = N - M + 1; --i; ++p) *p = (p[M] ^ twist(p[0], p[1])); for(i = M; --i; ++p) *p = (p[M - N] ^ twist(p[0], p[1])); *p = p[M - N] ^ twist(p[0], state[0]); left = N; next = state; }
size_t mixbits(size_t u, size_t v) const { return (u & 2147483648UL) | (v & 2147483647UL); }
size_t twist(size_t u, size_t v) const { return ((mixbits(u, v) >> 1) ^ (v & 1UL ? 2567483615UL : 0UL)); }
static const int N = 624, M = 397; size_t state[N]; size_t left; size_t* next; };
class mtrand_help { static mtrandom r; public: mtrand_help() {} void operator()(size_t s) { r.reset(s); } size_t operator()() const { return r.rand(); } double operator()(double) { return r.real(); } }; mtrandom mtrand_help:: r;
extern void mtsrand(size_t s) { mtrand_help()(s); } extern size_t mtirand() { return mtrand_help()(); } extern double mtdrand() { return mtrand_help()(1.0); }
#endif // mtrandom_HPP_ |
以下为测试文件。
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 |
#include <iostream> #include <cstdlib> #include <algorithm>
using std::cout;
#include <windows.h> #include "mtrandom.h"
int main(int argc, char *argv[]) { const unsigned _SIZE = 100000;
int* a = new int[_SIZE];
mtrandom mtr = mtrandom(std::time(0));
cout<<"Generating random integers is in progress .../n/n"; DWORD count = GetTickCount(); for (int i = 0; i < _SIZE; ++i) a[i] = mtr.rand(); cout<<"Generating "<<_SIZE<<" random integers only used "; cout<<GetTickCount() - count<<" milliseconds./n/n";
cout<<"Sorting random integers is in progress .../n/n"; count = GetTickCount(); std::sort(&a[0], &a[_SIZE]); cout<<"std::sort() sorting "<<_SIZE<<" random integers used "; cout<<GetTickCount() - count<<" milliseconds!/n/n";
delete [] a;
system("PAUSE"); return EXIT_SUCCESS; } |
结果如下图所示:
由图可以得知,sort是最快的排序算法,深入其底层源代码我们发现,sort其实是一种混合排序,采用基本的分割算法,当pivot偏向一边的时候自动转化为堆排序,当已经基本分割成较小的片段基本有序时,采用插入排序,这样得以保持很快的排序速度。stable_sort,稳定排序,采用归并排序和,基本有序的时候采用插入排序,消耗内存较多,如果没有足够的内存则退化成O(nlogn * logn)的时间复杂度。qsort,C语言的初学者对他应该很熟悉,采用三点取中的办法获取pivot,但是最坏的情况下退化成O(n * n)的时间复杂度,本测试采用随机数,应该是最理想的,但是他速度并不是很快,主要原因是本身设计问题,由于比较需要频繁的调用比较函数,导致速度大大下降。heap_sort虽然时间复杂度仍旧是O(nlogn),但是速度很慢,不过需要的额外内存很少。本来我还想好好分析一下STL底层源码的实现的,但是摊开侯捷伯伯的《STL源码剖析》,已经将这些算法的源码来龙去脉讲解得一清二楚了,要想再说得更明白更精彩很难呀~呵呵。