Mar*_*nas 2 java benchmarking mergesort quicksort
我已经尝试过基准测试,出于某种原因,当在 1M 元素的数组上尝试它们时,它们在0.3 秒内Mergesort排序并Quicksort花费了 1.3 秒。
我听说快速排序通常更快,因为它的内存管理,但如何解释这些结果?
如果这有什么不同,我正在运行 MacBook Pro。输入是一组从 0 到 127 的随机生成的整数。
代码在Java中:
归并排序:
static void mergesort(int arr[]) {
int n = arr.length;
if (n < 2)
return;
int mid = n / 2;
int left[] = new int[mid];
int right[] = new int[n - mid];
for (int i = 0; i < mid; i++)
left[i] = arr[i];
for (int i = mid; i < n; i++)
right[i - mid] = arr[i];
mergesort(left);
mergesort(right);
merge(arr, left, right);
}
public static void merge(int arr[], int left[], int right[]) {
int nL = left.length;
int nR = right.length;
int i = 0, j = 0, k = 0;
while (i < nL && j < nR) {
if (left[i] <= right[j]) {
arr[k] = left[i];
i++;
} else {
arr[k] = right[j];
j++;
}
k++;
}
while (i < nL) {
arr[k] = left[i];
i++;
k++;
}
while (j < nR) {
arr[k] = right[j];
j++;
k++;
}
}
Run Code Online (Sandbox Code Playgroud)
快速排序:
public static void quickSort(int[] arr, int start, int end) {
int partition = partition(arr, start, end);
if (partition - 1 > start) {
quickSort(arr, start, partition - 1);
}
if (partition + 1 < end) {
quickSort(arr, partition + 1, end);
}
}
public static int partition(int[] arr, int start, int end) {
int pivot = arr[end];
for (int i = start; i < end; i++) {
if (arr[i] < pivot) {
int temp = arr[start];
arr[start] = arr[i];
arr[i] = temp;
start++;
}
}
int temp = arr[start];
arr[start] = pivot;
arr[end] = temp;
return start;
}
Run Code Online (Sandbox Code Playgroud)
您的实现有点简单:
mergesort 在每次递归调用时分配 2 个新数组,这很昂贵,但一些 JVM 在优化此类编码模式方面非常有效。quickSort 使用了一个糟糕的主元选择,子数组的最后一个元素,它为排序的子数组提供了二次时间,包括那些具有相同元素的子数组。数据集,一个小范围内的伪随机数数组0..127,导致实现的缺点quickSort比mergesort版本的低效性能差很多。增加数据集大小应该会使这一点更加明显,甚至可能由于递归调用过多而导致堆栈溢出。具有共同模式(例如相同值、增加或减少集合以及此类序列的组合)的数据集将导致实现的灾难性性能quickSort。
这是一个稍微修改的版本,其中枢轴的病态选择较少(数组的 3/4 处的元素)和用于检测枢轴值重复的循环,以提高具有重复值的数据集的效率。在我的标准排序基准测试中,它的性能要好得多(100 倍),数组只有 40k 个元素,但仍然比基数排序慢得多(8 倍):
public static void quickSort(int[] arr, int start, int end) {
int p1 = partition(arr, start, end);
int p2 = p1;
/* skip elements identical to the pivot */
while (++p2 <= end && arr[p2] == arr[p1])
continue;
if (p1 - 1 > start) {
quickSort(arr, start, p1 - 1);
}
if (p2 < end) {
quickSort(arr, p2, end);
}
}
public static int partition(int[] arr, int start, int end) {
/* choose pivot at 3/4 or the array */
int i = end - ((end - start + 1) >> 2);
int pivot = arr[i];
arr[i] = arr[end];
arr[end] = pivot;
for (i = start; i < end; i++) {
if (arr[i] < pivot) {
int temp = arr[start];
arr[start] = arr[i];
arr[i] = temp;
start++;
}
}
int temp = arr[start];
arr[start] = pivot;
arr[end] = temp;
return start;
}
Run Code Online (Sandbox Code Playgroud)
对于 OP 的数据集,假设分布具有良好的随机性,扫描重复项是提高性能的原因。正如预期的那样,选择不同的枢轴,无论是第一个、最后一个、中间、3/4 或 2/3,甚至 3 的中位数几乎没有影响。
quickSort由于选择了枢轴,对其他非随机分布的进一步测试显示了此实现的灾难性性能。在我的基准测试中,通过选择将元素置于数组的 3/4 或 2/3 处(50k 样本提高 300 倍,比标准归并排序快 40%,时间与 相当radix_sort)获得了显着提高的性能。
| 归档时间: |
|
| 查看次数: |
261 次 |
| 最近记录: |