【问题标题】:Why does Java sort outperform Counting sort for primitives为什么Java排序优于原语的计数排序
【发布时间】:2020-04-20 00:02:02
【问题描述】:

我正在比较计数排序与 Java 原生 Arrays.sort 的运行时间。从我读过的内容来看,计数排序提供了最佳、平均和最坏情况下的 n+k 运行时间。 Javas Arrays 类型的原语,使用双轴快速排序,是一种基于比较的算法,因此在平均情况下必须提供 O(n log n),而在最坏情况下提供 On2。

当通过测量对大小范围为 500 到 100k 的一系列数组进行排序所花费的时间(纳秒)来比较两者时,我注意到当大小达到 ~70k 时,计数排序的运行时间急剧增加。

我的理解是计数排序是有效的,只要输入数据的范围不明显大于要排序的元素数 数组是从 0 到 99 之间的随机数构建的,所以 k 总是比 n 小很多。

随着 n 的增加,计数排序会突然退化有什么特别的原因吗?

我的计数排序实现:

public static int[] countSort(int[] arr, int k) {
        /*
         * This will only work on positive integers 0 to K.
         * For negative  worst case testing we use the negative count sort below.
         * 
         * Step 1: Use an array to store the frequency of each element. For array
         * elements 1 to K initialize an array with size K. Step 2: Add elements of
         * count array so each element stores summation of its previous elements. Step
         * 3: The modified count array stores the position of elements in actual sorted
         * array. Step 5: Iterate over array and position elements in correct position
         * based on modified count array and reduce count by 1.
         */

        int[] result = new int[arr.length];
        int[] count = new int[k + 1];
        for (int x = 0; x < arr.length; x++) {
            count[arr[x]]++;
        }

        /*
         * Store count of each element in the count array Count[y] holds the number of
         * values of y in the array 'arr'
         */

        for (int y = 1; y <= k; y++) {
            count[y] += count[y - 1];
        }

        /*
         * Change count[i] so that count[i] now contains actual Position of this element
         * in result array
         */

        for (int i = arr.length - 1; i >= 0; i--) {
            result[count[arr[i]] - 1] = arr[i];
            count[arr[i]]--;
        }

        System.out.println("COUNTSORT SORTED ARRAY = " + Arrays.toString(result));
        return result;

    }

分辨率: 根据@Alex 的建议运行 Counting 排序会产生更优越的运行时间。

【问题讨论】:

  • 嘿!是为什么会出现尖峰的问题,还是 Java 排序优于自定义排序的问题? 仅供参考: 请注意,Java 排序是高度优化的,并且可能使用本机系统方法。你不能用“正常”的代码打败它。我试过了。
  • Java 不再使用快速排序。它使用 Tim 排序。还是双轴快速排序?
  • 您用于测试的硬件规格是什么?
  • 嗨,它是 2.9Ghz i7 8 GB 1600 MHz DDR3 MacBook pro

标签: java performance sorting counting-sort


【解决方案1】:

只是一个猜测,但您的排序算法使用的内存比 Java 的多。 70k 个整数是 280KB。你需要双倍的空间,超过 512KB。根据所使用的处理器,这可能会在(L1?)缓存中运行排序和有很多缓存未命中之间产生差异。由于您实际上并不需要副本,因此请进行适当的排序。如果你现在碰壁,你就有答案了。

编辑:它是 280KB。

Edit2:昨天很晚,所以这里是就地版本。请注意,它会修改输入数组。

public static int[] countSortRefactored(int[] arr, int k) {
    int[] count = new int[k + 1];
    for (int x = 0; x < arr.length; x++) {
        count[arr[x]]++;
    }

    int idx=0;
    for (int x=0; x<=k; x++) {
        Arrays.fill(arr, idx, idx+=count[x], x);
    }

    System.out.println("COUNTSORT SORTED ARRAY = " + Arrays.toString(arr));
    return arr;
}

【讨论】:

  • 嗨 Axel,它是英特尔 i7 双核,每核 L2 256kb,L3 4MB。性能下降是否可以归结为从 L2 切换到 L3?
  • 是的,我就是这么想的。
  • 您提到了就地执行排序,这是如何实现的?
  • 关于缓存未命中的要点;由于快速排序具有近乎顺序的访问模式,因此与在整个地方进行随机访问的计数排序相比,它应该经历更少的缓存未命中。这可能解释了为什么快速排序更快,即使它执行的指令要多得多。
  • 请尝试我刚刚提供的示例代码。我真的很感兴趣这有什么不同,以及在你的情况下它与 Java 的 Timsort 相比如何。
猜你喜欢
  • 2013-08-29
  • 1970-01-01
  • 2017-07-23
  • 2011-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-24
  • 1970-01-01
相关资源
最近更新 更多