为什么使用函数生成随机数导致速度较慢的快速排序?

时间:2015-11-17 00:27:11

标签: c++ sorting quicksort

我想生成1000000个随机数,并使用快速排序算法对它们进行排序。有两个不同的程序:

// Program 1
void quicksort()
{
   // ...
}

int main()
{
    int *arr = new int[1000000];

    // generate random number in main()
    std::default_random_engine e(100);
    std::uniform_int_distribution<unsigned> u(1,10000);
    for(int i = 0;i < 999999;++i)
       arr[i] = u(e);

    clock_t start = clock();
    quicksort(arr,0,999999);
    clock_t end = clock();
    cout<<"time:"<<static_cast<float>(end-start)/CLOCKS_PER_SEC<<endl;
    delete [] arr;
    return 0;
}

输出:time:0.361684

// Program 2
void quicksort()
{
       // ...
}

void generateRandom(int *arr,int size,int seed)
{
   std::uniform_int_distribution<unsigned> u(0,1000);
   std::default_random_engine e(seed);
   for(int i = 0; i < size; ++i)
       arr[i] = u(e);
}

int main()
{
        int *arr = new int[1000000];

        generateRandom(arr,1000000,100);  // The only different between Program1 and Program2

        clock_t start = clock();
        quicksort(arr,0,999999);
        clock_t end = clock();
        cout<<"time:"<<static_cast<float>(end-start)/CLOCKS_PER_SEC<<endl;
        delete [] arr;
        return 0;
}

输出:time: 1.88307
为什么使用generateRandom()生成随机数导致快速排序更慢?Here是完整的程序 谢谢你的帮助。

2 个答案:

答案 0 :(得分:3)

您只计算对快速排序的调用,这会将时差区分为仅对已生成的数字进行排序的工作。

快速排序的运行时间根据其输入而变化。在最坏的情况下,Quicksort在O(n**2)中运行。平均O(n log n)。例如,如果快速排序实现选择第一个可用元素作为数据透视表,那么最坏的情况是给它一个已经排序的数组,因为需要更多的交换。

由于您的输入不同,而不是因为您在函数与内联中生成数字,因此您在时间上会有所不同。您的生成器在两个程序中使用相同的种子,但您使用的是不同的分布(1,1000)vs(1,10000) - 这将导致一组完全不同的整数。

均匀分布中的较小扩展将减少数组中的熵(例如,将存在更多重复值),这将影响为完全排序数组而必须执行的交换次数。数组中的初始相对排序将影响整数在所选枢轴周围移动的次数。

在两种情况下(一个线性阵列),内存中生成的数字的布局是相同的,并且程序的占用空间足够小,以至于我们可以安全地排除代码缓存未命中,从而导致内部运行时间的差异快速通话。您的总运行时间将受到您正在进行的内存比较和交换次数的影响(以及您正在进行的少量缓存未命中 - 您有4MiB的数字需要排序,它不是很多) 。我假设quicksort()中的代码都是相同的。

编辑:

为了说明问题,您可以按如下方式修改程序:

for(int i = 0;i < 999999;++i)
  arr[i] = i; //u(e);

完全放弃随机生成。这使得你的快速排序算法可以在已排序的数组上运行 - 这是最糟糕的情况。

在我的系统上,尝试在函数内部生成数字的一些版本在1到2秒内完成(因为它显示在外部代码链接中),而使用排序版本完成的时间要长得多多少时间。将已经排序的数字排序从0到100000(而不是一百万)单独排序需要15秒。

(编辑:稳定/不稳定算法都受重复影响。感谢@rcgldr)

答案 1 :(得分:1)

问题是链接到示例中使用的分区方法。它使用类似Lomuto的分区方案,而不是Hoare partition scheme。我使用Visual C / C ++ express 2010版本进行了测试,结果更糟糕,1&gt; 10000为0.1秒,1-> 100为2.7秒。

在下面的示例代码中,我使用了一个Hoare分区方案,结合了三个中间值用于数据透视,时间随着更多重复或有序数据而提高。

在我的系统上,英特尔2600K,3.4ghz,使用Visual C / C ++ express 2010发布构建,排序10,000,000个整数。快速排序的这种变化花费0.531秒,分布1-> 10000,0.469,其中1-> 1000,0.375,1-> 100,和0.109,已经分类的数据。 clock()基于64hz自动收报机,所以时间为+/- 0.015625秒。

typedef int int32_t;

void quicksort(int32_t a[], int lo, int hi) {
    int i = lo, j = (lo + hi)/2, k = hi;
    int32_t pivot;
    if (a[k] < a[i])            // median of 3
        std::swap(a[k], a[i]);
    if (a[j] < a[i])
        std::swap(a[j], a[i]);
    if (a[k] < a[j])
        std::swap(a[k], a[j]);
    pivot = a[j];
    while (i <= k) {            // partition
        while (a[i] < pivot)
            i++;
        while (a[k] > pivot)
            k--;
        if (i <= k) {
            std::swap(a[i], a[k]);
            i++;
            k--;
        }
    }
    if (lo < k)                 // recurse
        quicksort(a, lo, k);
    if (i < hi)
        quicksort(a, i, hi);
}
相关问题