为啥我的分拣程序这么慢? (java中的基数/桶排序)

Posted

技术标签:

【中文标题】为啥我的分拣程序这么慢? (java中的基数/桶排序)【英文标题】:Why is my sorting program so slow? (radix/bucket sort in java)为什么我的分拣程序这么慢? (java中的基数/桶排序) 【发布时间】:2016-03-12 16:57:37 【问题描述】:

这是一个基数/桶排序混合,硬编码为 9 位数字。我的快速排序程序对 1000 万个数字进行排序的速度是原来的两倍多。我已经验证输出是正确的,只是速度很慢。

代码:

public static void main(String[] args) 
    Scanner in = new Scanner(System.in);
    ArrayList<Integer> inputs = new ArrayList<>();
    while (in.hasNext()) 
        inputs.add(in.nextInt());
    
    radixSort(inputs);
    //System.out.print(toString(radixSort(inputs)));


public static ArrayList<Integer> radixSort(ArrayList<Integer> a) 
    for (int i = 1; i <= 9; i++) 
        a = bucketSort(a, i);
    
    return a;


public static ArrayList<Integer> bucketSort(ArrayList<Integer> a, int index) 
    // Creates buckets
    ArrayList<ArrayList<Integer>> b = new ArrayList<ArrayList<Integer>>();
    for (int i = 0; i < 10; i++) 
        b.add(new ArrayList<Integer>());
    
    // Sorts into buckets
    for (int i = 0; i < a.size(); i++) 
        b.get(key(a.get(i), index)).add(a.get(i));
    
    // Concatenates buckets
    ArrayList<Integer> c = new ArrayList<>();
    for (int i = 0; i < b.size(); i++) 
        c.addAll(b.get(i));
    
    return c;


// Takes an integer and index and returns digit at index
public static int key(int num, int ind) 
    int digit = num / (int)Math.pow(10, ind - 1);
    digit = digit % 10;
    return (int)digit;


public static String toString(ArrayList<Integer> a) 
    StringBuilder s = new StringBuilder();
    for (int i = 0; i < a.size(); i++)
        s.append(String.format("%09d\n", a.get(i)));
    
    return s.toString();

【问题讨论】:

我还没有检查实现,但这似乎并没有什么不同。快速排序是O(n log(n)),而基数是O(wn)。对于 9 位数字,您的 w 大约是 29log(10000000) = 7,因此进行的比较减少了大约 4 倍。 【参考方案1】:

缓慢的主要原因是每次向每个桶数组追加一个整数,必须再次追加才能连接桶,这涉及动态扩展数组。

最低有效位第一桶排序的计数变化会一次性分配与原始数组大小相同的第二个数组。对于 9 位示例,它可以为每个数字生成 '0'、'1'、...'9' 的出现次数,然后将计数转换为每个大小变量开头的起始索引桶,消除了连接的需要。对于 9 位示例,矩阵 [9][10] 可用于计数/索引,因此仅使用一次传递即可生成矩阵。

维基文章:

http://en.wikipedia.org/wiki/Counting_sort

示例 C++ 代码使用字节大小的“数字”对 32 位无符号整数数组进行排序,因此计数/索引矩阵为 [4][256]。其中唯一的 C++ 部分是 std::swap(),否则是 C 代码。

typedef unsigned int uint32_t;

//  a is input array, b is working array
uint32_t * RadixSort(uint32_t * a, uint32_t *b, size_t count)

size_t mIndex[4][256] = 0;            // count / index matrix
size_t i,j,m,n;
uint32_t u;
    for(i = 0; i < count; i++)         // generate histograms
        u = a[i];
        for(j = 0; j < 4; j++)
            mIndex[j][(size_t)(u & 0xff)]++;
            u >>= 8;
               
    
    for(j = 0; j < 4; j++)             // convert to indices
        m = 0;
        for(i = 0; i < 256; i++)
            n = mIndex[j][i];
            mIndex[j][i] = m;
            m += n;
               
    
    for(j = 0; j < 4; j++)             // radix sort
        for(i = 0; i < count; i++)     //  sort by current lsb
            u = a[i];
            m = (size_t)(u>>(j<<3))&0xff;
            b[mIndex[j][m]++] = u;
        
        std::swap(a, b);                //  swap ptrs
    
    return(a);

【讨论】:

以上是关于为啥我的分拣程序这么慢? (java中的基数/桶排序)的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的基数排序 python 实现比快速排序慢?

为啥我的haskell程序这么慢? Haskell 编程,人生游戏

为啥我的 Docker 容器中的网络/互联网这么慢?

为啥我的代码这么慢?

为啥 Tkinter 窗口打开这么慢?

为啥我的手动调优、支持 SSE 的代码这么慢?