为什么我的合并排序不像O(n * lg n))?

时间:2014-04-19 04:22:22

标签: c++ performance algorithm sorting

我基于Cormen's Book上提供的伪代码实现此合并排序。我正在复制它,因为它简短:

void merge(vector<double> &array, int start, int mid, int end) {
    int i = start;
    int j = mid + 1;
    int k = start;
    vector<double> b(array.size());

    while (i <= mid && j <= end) {
        if (array[i] <= array[j])
            b[k++] = array[i++];
        else
            b[k++] = array[j++];
    }

    while(i <= mid)
        b[k++] = array[i++];

    while(j <= end)
        b[k++] = array[j++];

    for (k = start; k <= end; k++)
        array[k] = b[k];
}

这部分应为O(n)

而另一个应该是O(n * lg n),其中lg是登录2 base

void mergeSort(vector<double> &array, int start, int end) {
    if (start < end) {
        int mid = (end - start) / 2 + start;
        mergeSort(array, start, mid);
        mergeSort(array, mid + 1, end);
        merge(array, start, mid, end);
    }
}

我用大小为1000(10 ^ 3),10000(10 ^ 4),50000(5 * 10 ^ 4),100000(10 ^ 5),250000(2.5 * 10 ^)的随机向量实例进行了一些实验。 5),500000(5 * 10 ^ 5)。每种规模有30个实例。 这是我对每个实例大小的平均结果:

1000 - ~0.000 s
10000 - 0.344 s
50000 - 20.456 s
100000 - 59.083 s
250000 - 360.814 s
500000 - 1729.245 s

我在运行合并排序时从linux time命令(占用用户时间)中获取的所有时间。 如可见,它不是O(n * lg n)行为。我在这里失踪了什么? 我不知道它是否相关,但我的系统配置是:

OS: Fedora 18 - 64 bits
Processor: Intel® Core™ i3 CPU M 380 @ 2.53GHz × 4
Memory: 2.8 Gi

2 个答案:

答案 0 :(得分:7)

这是罪魁祸首:

vector<double> b(array.size());

假设您从一个包含五十万个条目的向量开始。对mergeSort的初始调用将在一个包含五十万个条目的向量上调用mergeSort,但只对它排序前250,000个元素。 (然后它将在下一半重复。)下一次调用mergeSort将接收完整的500,000个元素数组并调用mergeSort以使用排序数组的第一个和第二个125,000个元素。等等。每次沿途,mergeSort将接收50万个条目的向量,但只对子集进行排序。最后,您将调用merge,其中每个呼叫将分配和初始化一个包含五十万个元素的临时数组。

结果是n 2 * log(n)行为。这不是指数行为,但仍然不好。

我看到三种不同的解决方案:

  • 将该临时b分配一次,并将其作为参数传递给mergeSortmerge
  • merge中分配大小为end-start+1的临时数组。现在,您必须使用抵消来处理b[0]array[start]对应的事实。
  • 合并到位。你不需要临时的。然而,这是非常重要的并且将使算法成为O(N *(log(N))^ 2)算法。

答案 1 :(得分:2)

似乎relocation of the vectors占用了大部分时间。添加到向量不是O(1)操作。尝试将矢量更改为基本的C类型数组,您会发现差异。而且,从价值观看,我不是指数级的。可能是更高的多项式。

相关问题