对于具有不同顺序的数据,基数排序算法可能具有不同的运行时间?

时间:2015-11-26 00:37:00

标签: c algorithm sorting radix-sort

我正在尝试实现基数排序算法。 我使用链表来存储要排序的元素,然后我将每个元素都抛到它的桶中。这个桶只是一个指针,它将链接属于其存储桶的元素列表。

我正在区间[0,1000000]中测试10.000.000和100.000.000个整数。这个数字可以是新月,渐弱和随机顺序。

新月和新月订单中100.000.000个数字的运行时间约为20秒。但对于具有随机顺序的相同数量的元素 运行时间约为110秒。

据我了解,该算法对于任何质量都具有相同的复杂性 要排序的数据。

任何人都知道为什么会这样吗?

这是我的代码:

    void radix(Number** numbers)
    {
        unsigned int i, k, e = 1;
        Number* bucket[10];
        Number* tail[10];
        Number* index;

        for(k = 0; k < 7; k++, e *= 10)
        {
            for(i = 0; i < 10; i++) bucket[i] = tail[i] = NULL;

            index = *numbers;
            while(index != NULL)
            {
                i = (index->value / e) % 10;

                if(tail[i] == NULL)
                    bucket[i] = index;
                else
                    tail[i]->next = index;

                tail[i] = index;
                index = index->next; 
            }

            for(i = 0; i < 10; i++)
            {
                if(tail[i] != NULL)
                {
                    *numbers = bucket[i];
                    index = tail[i];
                    for(i++; i < 10; i++)
                    {
                        if(tail[i] != NULL)
                        {
                            index->next = bucket[i];
                            index = tail[i];
                        }
                    }
                }
            }

            index->next = NULL;
        }
    }

其中Number是:

typedef struct number
{
    unsigned int value;
    struct number* next;
} Number;

1 个答案:

答案 0 :(得分:2)

答案可能与内存访问和引用位置有关。

升序/降序有一个规则的模式,它可能具有更大的时间局部性,而不是关于桶,但可能更多关于链表节点用于数字的方式(特别是如果它们不是不连续的。

例如,如果我们接受输入:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, ...

我们从桶0循环到桶9然后回到桶0.当我们回到桶0时,我们正在访问最近访问过的number节点(仅仅9次迭代)可能会被缓存到更快,更小的内存中。

如果我们使用随机排序,谁知道我们何时会回到0?因此,在我们回到用于任何给定存储桶头部的数据的内存之前,我们有更长的时间将数据从DRAM移动到缓存。结果可以转化为更多此类前number节点从缓存中逐出,并且当我们回到这样的存储桶时会有更多缓存未命中。

对于不规则排序,分支误预测也可能消耗一些时间。分析应该有助于缩小原因。

如果您确实存在内存瓶颈,可以尝试的一件事就是将您的存储桶转换为,例如,您将深度复制数字的展开列表。这将使你不再访问以前插入的数字的内存,这些数字可能已被插入多次迭代(由于随机排序而成为一个潜在的大变量)。有了这个,我们开始回到一些时间局部性(如果数字是连续分配的话可能是空间局部性),否则我们将失去这种链表表示。然后,重新使用桶的连续内存(其中只有10个)而不是桶之间的元素,其间具有可变步幅。我们还通过展开的表示在桶内获得空间局部性。

  

但如果是相同的数据,只是订单不同,这会影响到   很多?对于相同的数据,20到110秒太多了。

内存效率可以在数量级上产生差异。 http://lwn.net/Articles/250967/

我不是这个主题的专家(更多的是“简介它并尝试根据指南进行优化”类型),但根据过去的结果我从内存优化中获得,我会经常把它们放在一边在算法优化的效果方面。例外情况是复杂性的差异是粗略的(例如,线性与二次),但即使是线性算法也非常可行地击败具有非常大输入的线性算法,如果前者对缓存更友好的话。