采纳答案成功!
向帮助你的同学说点啥吧!感谢那些助人为乐的人
快速排序和归并排序(优化版)用的都是老师您的代码哦= =
基本上测试结果:
归并排序都是0.2左右
快速排序都是0.9左右
处理的数据规模是1000000
差距就这么大!加上随机化试试看。简单的一步,效率就比归并快了。随机化那一步是快速排序的精髓。不然快排无法保证递归深度是logn这个级别。关于快排的时间复杂度分析,由于这步随机化,所以准确的分析非常难,需要的数学知识太多了,我在课程中没有讲。你可以简单地理解:没有随机化,整个快排的递归树将严重有偏;而归并排序的递归树,则一定保持着深度是logn这个级别的。这也是我在回答中说:每次随机选取pivot,在真正的快排实现中“必须“加入。我强调“必须“的原因。
另外,这种“有偏“是测试用例相关的。如果递归树恰好不那么偏,快排就会表现的比较快。但要明白这种快是一种“侥幸”。不管怎样,随机化是快排中标准的步骤,让快排在统计意义上一跃成为最快的排序算法。必须加入。
我这边也是这样的问题,归并排序效率比快速排序效率高(见下图, 100w的随机数据):
需要说明的是,我的代码中没有用标准库中的swap函数,因为效率更低,而是自己写了一个简单的交换函数
1 2 3 4 5 6 | template < typename T> void _swap(T &a, T &b) { int tmp = a; a = b; b = tmp; } |
// Quicksort, following Bentley and McIlroy,
// ``Engineering a Sort Function,'' SP&E November 1993.
上面这篇论文给出了cpu运算的执行时间,如下图
从图中可以看出,标准库里面的swap函数效率很低,是赋值50多倍,而即使是自己写的交换函数,效率也是比较低的。
归并排序中没有用到交换,而快排中主要操作就是交换,会不会是这个原因导致效率下降的呢?
就是这样的!赞研究精神。 不过自己写swap,会丧失编译器优化的性能。如果是使用VS做的实验,试试看在release模式下测试算法的性能?具体见这个问题: http://coding.imooc.com/learn/questiondetail/3603.html
如果使用VS做算法性能测试,请确保是在release模式下运行哦。具体可以看这个问题:http://coding.imooc.com/learn/questiondetail/3603.html
如果使用VS做算法性能测试,请确保是在release模式下运行哦。具体可以看这个问题:http://coding.imooc.com/learn/questiondetail/3603.html
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 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | #include<stdio.h> #include<iostream> #include<stdlib.h> #include<time.h> #include"SortTestHelper.h" #define True 1 template < typename T> void insertionSort(T arr[], int l, int r) { for ( int i = l + 1; i <= r; i++) { T e = arr[i]; int j; for (j = i; j > l && arr[j - 1] > e; j--) arr[j] = arr[j - 1]; arr[j] = e; } return ; } //=================================================================== template < typename T> void __merge(T arr[], int l, int mid, int r) { T *aux= new T[r-l+1]; for ( int i = l; i <= r; i++) aux[i - l] = arr[i]; int i = l, j = mid + 1; for ( int k = l; k <= r; k++) { if (i > mid) { arr[k] = aux[j - l]; j++; } else if (j > r) { arr[k] = aux[i - l]; i++; } else if (aux[i - l] < aux[j - l]) { arr[k] = aux[i - l]; i++; } else { arr[k] = aux[j - l]; j++; } } delete [] aux; } template < typename T> void __mergeSort(T arr[], int l, int r) { if (r - l <= 15) { insertionSort(arr, l, r); return ; } int mid = (l + r) / 2; __mergeSort(arr, l, mid); __mergeSort(arr, mid + 1, r); if (arr[mid] > arr[mid + 1]) __merge(arr, l, mid, r); } template < typename T> void mergeSort(T arr[], int n) { __mergeSort(arr, 0, n - 1); } //======================================================================= //是对arr[l...r]进行partition操作 //返回p,使得arr[l...p-1]<arr[p];arr[p+1]>arr[p] template < typename T> int __partition(T arr[], int l, int r) { swap(arr[l], arr[ rand () % (r - l + 1) + l]); T v = arr[l]; int j = l; for ( int i = l + 1; i <= r; i++) if (v > arr[i]) swap(arr[++j], arr[i]); swap(arr[j], arr[l]); return j; } //对arr[l...r]部分进行快速排序 template < typename T> void __quicksort(T arr[], int l, int r) { //if (l >= r) // return; if (r - l <16) { insertionSort(arr, l, r); return ; } int p = __partition(arr, l, r); __quicksort(arr, l, p - 1); __quicksort(arr, p + 1, r); return ; } //快速排序! template < typename T> void quick_sort(T arr[], int n) { srand (( int ) time (NULL)); __quicksort(arr, 0, n - 1); } //============================================================= //是对arr[l...r]进行partition操作 //返回p,使得arr[l...p-1]<arr[p];arr[p+1]>arr[p] template < typename T> int __partition2(T arr[], int l, int r) { swap(arr[l], arr[ rand () % (r - l + 1) + l]); T v = arr[l]; //arr[l+1...i)部分小于v,arr(j...r]部分大于v int i = l + 1, j = r; while (True) { while (i <= r&&arr[i] < v) i++; while (j >= l + 1 && arr[j] > v) j--; if (i > j) break ; swap(arr[i], arr[j]); i++; j--; } swap(arr[l], arr[j]); return j; } //对arr[l...r]部分进行快速排序 template < typename T> void __quicksort2(T arr[], int l, int r) { //if (l >= r) // return; if (r - l <16) { insertionSort(arr, l, r); return ; } int p = __partition2(arr, l, r); __quicksort2(arr, l, p - 1); __quicksort2(arr, p + 1, r); return ; } //快速排序! template < typename T> void quick_sort2(T arr[], int n) { srand (( int ) time (NULL)); __quicksort2(arr, 0, n - 1); } //测试快速排序和归并排序的运行速度 int main() { //int time = 0,avm=0,avq=0,avq2=0; int *arr1, *arr2, *arr3; int n = 1000000; //while (time < 1000000) //{ arr1 = SortTestHelper::generateRandomArray(n, 0, n); arr2 = SortTestHelper::copyIntArray(arr1, n); arr3 = SortTestHelper::copyIntArray(arr1, n); /*Guibing_BU_sort(arr1, n); quick_sort(arr2, n);*/ //快排若没有随机化则会退化到o(n**2)级的算法 SortTestHelper::testSort( "merge sort" , mergeSort, arr1, n); SortTestHelper::testSort( "quick sort" , quick_sort, arr2, n); SortTestHelper::testSort( "quick sort2" , quick_sort2, arr3, n); delete [] arr1; delete [] arr2; delete [] arr3; /*}*/ putchar ( '\n' ); int swaptime = 100; arr1 = SortTestHelper::generateNearlyOrderedArray(n, swaptime); arr2 = SortTestHelper::copyIntArray(arr1, n); arr3 = SortTestHelper::copyIntArray(arr1, n); SortTestHelper::testSort( "merge sort" , mergeSort, arr1, n); SortTestHelper::testSort( "quick sort" , quick_sort, arr2, n); SortTestHelper::testSort( "quick sort2" , quick_sort2, arr3, n); delete [] arr1; delete [] arr2; delete [] arr3; putchar ( '\n' ); n = 1000000; arr1 = SortTestHelper::generateRandomArray(n, 0, 10); //arr2 = SortTestHelper::copyIntArray(arr1, n); arr3 = SortTestHelper::copyIntArray(arr1, n); /*Guibing_BU_sort(arr1, n); quick_sort(arr2, n);*/ //快排若没有随机化则会退化到o(n**2)级的算法 SortTestHelper::testSort( "merge sort" , mergeSort, arr1, n); //SortTestHelper::testSort("quick sort", quick_sort, arr2, n); SortTestHelper::testSort( "quick sort2" , quick_sort2, arr3, n); delete [] arr1; //delete[] arr2; delete [] arr3; putchar ( '\n' ); putchar ( '\n' ); system ( "pause" ); } |
插入,归并,用的都是您的代码!
难道是我电脑有问题?
我有我舍友的电脑测试了下,运行结果甚至比我的更差!
我在mac下实验你的这段代码,结果如下。目测vs实现的编译器有坑。。。 我在windows下试试看。。。 merge sort : 0.427345 s quick sort : 0.302787 s quick sort2 : 0.300788 s merge sort : 0.122907 s quick sort : 0.205381 s quick sort2 : 0.119227 s merge sort : 0.258978 s quick sort2 : 0.148382 s
我在vs下试验了一下,也是这种问题,然后搞明白了。因为你是在debug模式下运行的程序,vs在debug模式下做了很多其他事情,主要集中在std中。快排使用了std::swap,我发现就是这个函数拖慢了速度。你测试一下,会发现自己写一个swap都比调用std::swap快。。。 但是如果你在release模式下运行程序,就不会有这些问题了!试试看!你的代码没有问题:) 赞!欢迎使用多语言实现这些算法!一定会有很大收获的:)
如果使用VS做算法性能测试,请确保是在release模式下运行哦。具体可以看这个问题:http://coding.imooc.com/learn/questiondetail/3603.html
老师我加上随机化了,但还是,改变不大
下面是源代码
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 | #include<stdio.h> #include<iostream> #include<stdlib.h> #include<time.h> #include"SortTestHelper.h" #include"Sort_collection.h" using namespace std; //是对arr[l...r]进行partition操作 //返回p,使得arr[l...p-1]<arr[p];arr[p+1]>arr[p] template < typename T> int __partition(T arr[], int l, int r) { swap(arr[l] , arr[ rand () % (r - l + 1) + l]); T v = arr[l]; int j = l; for ( int i = l + 1; i <= r; i++) if (v > arr[i]) swap(arr[++j], arr[i]); swap(arr[j], arr[l]); return j; } //对arr[l...r]部分进行快速排序 template < typename T> void __quicksort(T arr[], int l, int r) { //if (l >= r) // return; if (r - l <16) { charu_guibing_sort(arr, l, r); return ; } int p = __partition(arr, l, r); __quicksort(arr, l, p - 1); __quicksort(arr, p + 1, r); return ; } //快速排序! template < typename T> void quick_sort(T arr[], int n) { srand (( int ) time (NULL)); __quicksort(arr, 0, n - 1); } //测试快速排序和归并排序的运行速度 int main() { char c; do { int n = 1000000; int *arr1 = SortTestHelper::generateRandomArray(n, 0, n); int *arr2 = SortTestHelper::copyIntArray(arr1,n); /*Guibing_BU_sort(arr1, n); quick_sort(arr2, n);*/ //快排若没有随机化则会退化到o(n**2)级的算法 SortTestHelper::testSort( "merge sort" , Guibing_sort, arr1, n); SortTestHelper::testSort( "quick sort" , quick_sort, arr2, n); delete [] arr1; delete [] arr2; int swaptime = 100; arr1 = SortTestHelper::generateNearlyOrderedArray(n, swaptime); arr2 = SortTestHelper::copyIntArray(arr1, n); SortTestHelper::testSort( "merge sort" , Guibing_sort, arr1, n); SortTestHelper::testSort( "quick sort" , quick_sort, arr2, n); delete [] arr1; delete [] arr2; } while ((c = getchar ()) == '\n' ); system ( "pause" ); } |
我试了一下你的代码。其中的插入排序和归并排序用的我的代码,没有问题。检查一下你的charu_guibing_sort(arr, l, r)实现是否有问题? 你的这个程序和我的课程中这一小节的程序做的事情是一样的。运行一下我的代码,看看是不是也是这个结果? https://github.com/liuyubobobo/Play-with-Algorithms/tree/master/03-Sorting-Advance/06-Quick-Sort-Deal-With-Nearly-Ordered-Array
抱歉,我刚看到这个问题后来我就一直没有回复了。使用VS的同学,在做算法性能测试的时候,请一定使用Release模式运行程序!不然,VS会在Debug模式下的标准库中做一些其他事情,另外运行器也使用的是未优化的版本,从而拖慢算法的执行速度,使得算法性能的比较不准确。可以尝试测试一下,在VS Debug模式下,std::swap的速度非常慢,甚至比自己写的swap还要慢。而我们在归并排序中,没有使用std::swap,所以在debug模式下就会比快排还要快了。具体见这个问题: http://coding.imooc.com/learn/questiondetail/3603.html
登录后可查看更多问答,登录/注册