我是一名CS学生,目前正在进行一项结合MergeSort和InsertionSort的实验.可以理解,对于某个阈值,S,InsertionSort将比MergeSort具有更快的执行时间.因此,通过合并两种排序算法,将优化总运行时间.
但是,在多次运行实验后,使用1000的样本大小和不同大小的S,实验结果每次都没有给出确定的答案.这是获得更好结果的图片(请注意,结果的一半时间不是确定的):
现在,尝试使用样本大小为3500的相同算法代码:
最后,尝试使用样本大小为500,000的相同算法代码(请注意,y轴以毫秒为单位:
虽然逻辑上,当S <= 10时,Hybrid MergeSort会更快,因为InsertionSort没有递归开销时间.然而,我的迷你实验的结果却说不然. 目前,这些是教给我的时间复杂性: MergeSort:O(n log n) 插入排序:
>最佳案例:θ(n)
>最坏情况:θ(n ^ 2)
最后,我找到了一个在线资料来源:https://cs.stackexchange.com/questions/68179/combining-merge-sort-and-insertion-sort,其中说明:
Hybrid MergeInsertionSort:
>最佳案例:θ(n n log(n / x))
>最坏情况:θ(nx n log(n / x))
我想询问CS社区中是否有结果显示混合MergeSort算法比低于某个阈值S的正常MergeSort算法更好的证据,如果是,为什么?
非常感谢SO社区,这可能是一个微不足道的问题,但它确实会澄清我目前有关时间复杂性和东西的许多问题:)
注意:我使用Java来编写算法,运行时可能会受到java在内存中存储数据的方式的影响.
Java中的代码:
- public static int mergeSort2(int n,int m,int s,int[] arr){
- int mid = (n+m)/2,right=0,left=0;
- if(m-n<=s)
- return insertSort(arr,n,m);
- else
- {
- right = mergeSort2(n,mid,s,arr);
- left = mergeSort2(mid+1,m,arr);
- return right+left+merge(n,arr);
- }
- }
- public static int insertSort(int[] arr,int n,int m){
- int temp,comp=0;
- for(int i=n+1; i<= m; i++){
- for(int j=i; j>n; j--){
- comp++;
- comparison2++;
- if(arr[j]<arr[j-1]){
- temp = arr[j];
- arr[j] = arr[j-1];
- arr[j-1] = temp;
- }
- else
- break;
- }
- }
- return comp;
- }
- public static void shiftArr(int start,int[] arr){
- for(int i=m; i>start; i--)
- arr[i] = arr[i-1];
- }
- public static int merge(int n,int[] arr){
- int comp=0;
- if(m-n<=s)
- return 0;
- int mid = (n+m)/2;
- int temp,i=n,j=mid+1;
- while(i<=mid && j<=m)
- {
- comp++;
- comparison2++;
- if(arr[i] >= arr[j])
- {
- if(i==mid++&&j==m && (arr[i]==arr[j]))
- break;
- temp = arr[j];
- shiftArr(i,j++,arr);
- arr[i] = temp;
- if(arr[i+1]==arr[i]){
- i++;
- }
- }
- i++;
- }
- return comp;
- }
解决方法
我测试了自上而下和自下而上的合并排序,并且两者都需要大约42毫秒== 0.042秒来排序500,000个32位整数,而图表中的明显结果是在大约42秒而不是42毫秒时慢1000倍.我还测试了10,000,000个整数,需要1秒多的时间来排序.
在过去,使用C,我将自下而上合并排序与混合自下而上合并/插入排序进行比较,对于1600万(2 ^ 24 == 16,777,216)32位整数,混合排序比S快约8% == 16. S == 64略慢于S == 16. Visual Studio std :: stable_sort是自下而上合并排序的变体(临时数组是原始数组大小的1/2)和插入排序,并使用S == 32.
对于小型数组,插入排序比合并排序更快,这是缓存局部性和使用插入排序对小数组进行排序所需的更少指令的组合.对于伪随机数据和S == 16到64,插入排序的速度大约是合并排序的两倍.
随着阵列大小的增加,相对增益减小.考虑到自下而上合并排序的影响,在S == 16的情况下,仅优化了4个合并传递.在我的测试用例中,有2 ^ 24 == 16,216个元素,即4/24 = 1/6~ = 16.7%的传递次数,导致大约8%的改进(因此插入排序大约是合并的两倍)排序等待4次传球).仅合并排序的总时间约为1.52秒,混合排序的总时间约为1.40秒,对于仅需1.52秒的过程,增益为0.12秒.对于自顶向下合并排序,S == 16,将优化4个最深层次的递归.
更新 – 具有O(n log(n))时间复杂度的混合就地合并排序/插入排序的示例Java代码. (注意 – 由于递归,辅助存储仍然在堆栈上消耗.)就地部分是在合并步骤期间通过交换合并区域中的数据与合并区域中的数据来完成的.这不是一个稳定的排序(由于合并步骤中的交换,不保留相等元素的顺序).对500,000个整数进行排序大约需要1/8秒,因此我将其增加到1600万(2 ^ 24 == 16777216)个整数,这需要4秒多一点.如果没有插入排序,排序大约需要4.524秒,而使用S == 64的插入排序,排序大约需要4.150秒,增益大约为8.8%.在C中基本上使用相同的代码,改进较少:从2.88秒到2.75秒,增益约为4.5%.
- package msortih;
- import java.util.Random;
- public class msortih {
- static final int S = 64; // use insertion sort if size <= S
- static void swap(int[] a,int i,int j) {
- int tmp = a[i]; a[i] = a[j]; a[j] = tmp;
- }
- // a[w:] = merged a[i:m]+a[j:n]
- // a[i:] = reordered a[w:]
- static void wmerge(int[] a,int j,int w) {
- while (i < m && j < n)
- swap(a,w++,a[i] < a[j] ? i++ : j++);
- while (i < m)
- swap(a,i++);
- while (j < n)
- swap(a,j++);
- }
- // a[w:] = sorted a[b:e]
- // a[b:e] = reordered a[w:]
- static void wsort(int[] a,int b,int e,int w) {
- int m;
- if (e - b > 1) {
- m = b + (e - b) / 2;
- imsort(a,b,m);
- imsort(a,e);
- wmerge(a,e,w);
- }
- else
- while (b < e)
- swap(a,b++,w++);
- }
- // inplace merge sort a[b:e]
- static void imsort(int[] a,int e) {
- int m,w,x;
- int t;
- // if <= S elements,use insertion sort
- if (e - b <= S){
- for(n = b+1; n < e; n++){
- t = a[n];
- m = n-1;
- while(m >= b && a[m] > t){
- a[m+1] = a[m];
- m--;}
- a[m+1] = t;}
- return;
- }
- if (e - b > 1) {
- // split a[b:e]
- m = b + (e - b) / 2;
- w = b + e - m;
- // wsort -> a[w:e] = sorted a[b:m]
- // a[b:m] = reordered a[w:e]
- wsort(a,w);
- while (w - b > 2) {
- // split a[b:w],w = new mid point
- n = w;
- w = b + (n - b + 1) / 2;
- x = b + n - w;
- // wsort -> a[b:x] = sorted a[w:n]
- // a[w:n] = reordered a[b:x]
- wsort(a,b);
- // wmerge -> a[w:e] = merged a[b:x]+a[n:e]
- // a[b:x] = reordered a[w:n]
- wmerge(a,x,w);
- }
- // insert a[b:w] into a[b:e] using left shift
- for (n = w; n > b; --n) {
- t = a[n-1];
- for (m = n; m < e && a[m] < t; ++m)
- a[m-1] = a[m];
- a[m-1] = t;
- }
- }
- }
- public static void main(String[] args) {
- int[] a = new int[16*1024*1024];
- Random r = new Random(0);
- for(int i = 0; i < a.length; i++)
- a[i] = r.nextInt();
- long bgn,end;
- bgn = System.currentTimeMillis();
- imsort(a,a.length);
- end = System.currentTimeMillis();
- for(int i = 1; i < a.length; i++){
- if(a[i-1] > a[i]){
- System.out.println("Failed");
- break;
- }
- }
- System.out.println("milliseconds " + (end-bgn));
- }
- }