【问题标题】:Why does this optimization cause my merge sort to fail?为什么这种优化会导致我的合并排序失败?
【发布时间】:2020-04-18 06:47:48
【问题描述】:

我正在研究一种非递归合并排序,并且我提出了一种可以加快速度的优化。要点是,不是每次合并到临时缓冲区然后将其复制回数据位置,而是先在一个方向合并,然后再合并另一个方向。这应该可以完美运行,因为缓冲区大小相同且数据相同。

但是,当我尝试这个时,我的数组没有完全排序。有一些项目,有时在最后,有时在中间,是不合适的。我在下面的示例中包含了我用来测试我的代码的函数。

我尽我所能制作一个 MWE,但有几个辅助函数是测试所需的。

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

#define MIN(x, y) ((x) > (y) ? (y) : (x))
#define OPTIMIZE true // if true, then merge in alternating directions

void merge(int* src, int* dest, size_t start, size_t mid, size_t end);
void merge_sort(int* data, size_t length);

/* MERGESORT IMPLEMENTATION {{{1 */

void merge(int* src, int* dest, size_t start, size_t mid, size_t end) {
    int i, j, k;
    for (i = start, j = mid, k = start; i < mid && j < end; k++) {
        if (src[i] > src[j]) {
            dest[k] = src[j];
            j++;
        } else {
            dest[k] = src[i];
            i++;
        }
    }
    for (; i < mid; i++, k++) {
        dest[k] = src[i];
    }
    for (; j < end; j++, k++) {
        dest[k] = src[j];
    }
}

void merge_sort(int* data, size_t length) {
    int* buffer = malloc(length * sizeof(int));
    int swap = false;
    for (size_t i = 0; i < length; i++) {
        buffer[i] = data[i];
    }

#if OPTIMIZE
    for (size_t step = 1; step < length; step *= 2, swap = !swap) {
        int* src = swap ? buffer : data;
        int* dest = swap ? data : buffer;
        for (size_t i = 0; i < length - step; i += (step * 2)) {
            merge(src, dest, i, i + step, MIN(length, i + (step * 2)));
        }
    }
    if (swap) {
        for (size_t i = 0; i < length; i++) {
            data[i] = buffer[i];
        }
    }
#else
    for (size_t step = 1; step < length; step *= 2, swap = !swap) {
        int* src = data;
        int* dest = buffer;
        for (size_t i = 0; i < length - step; i += (step * 2)) {
            merge(src, dest, i, i + step, MIN(length, i + (step * 2)));
        }
        for (size_t i = 0; i < length; i++) {
            data[i] = buffer[i];
        }
    }
#endif

    free(buffer);
}

/* UTILITY FUNCTIONS {{{1 */

void check_sorted(int* data, size_t length) {
    for (size_t i = 0; i < length - 1; i++) {
        if (data[i] != i) {
            printf("%ld: %d\n", i, data[i]);
        }
    }
}

void shuffle(int* data, size_t length) {
    for (size_t i = 1; i < length; i++) {
        size_t index = rand() % (i + 1);
        int temp = data[index];
        data[index] = data[i];
        data[i] = temp;
    }
}

/* MAIN {{{1 */

int main() {
    size_t length = 200;
    int* data = malloc(length * sizeof(int));

    for (size_t i = 0; i < length; i++) {
        data[i] = (int)i;
    }
    shuffle(data, length);

    merge_sort(data, length);
    check_sorted(data, length);

    free(data);
    return 0;
}

【问题讨论】:

  • 这是我调试的方法。使用较小的length(8 到 15 之间的值就足够了)。调用shuffle 后,保存数组。然后排序和检查。如果检查失败,则打印保存的数组。这为您提供了一个示例数组,您知道该数组将失败。使用该数组,并使用调试器单步执行代码以查看发生了什么。
  • check_sorted 函数中有一个错误:i &lt; length - 1 应该是i &lt; length。代码没有检查数组的最后一个元素。

标签: c sorting optimization merge mergesort


【解决方案1】:

这似乎有效。 cmets 中记录的修复:

void merge_sort(int* data, size_t length) {
    int* buffer = malloc(length * sizeof(int));
    int swap = false;
    /*                                  ** removed the initial copy */

#if OPTIMIZE
    for (size_t step = 1; step < length; step *= 2, swap = !swap) {
        int* src = swap ? buffer : data;
        int* dest = swap ? data : buffer;
        size_t i;                       /* fix, using i in 2nd loop */
        for (i = 0; i < length - step; i += (step * 2)) {  /* fix (removed size_t) */
            merge(src, dest, i, i + step, MIN(length, i + (step * 2)));
        }
        for( ; i < length; i++)         /* fix, copy single run if present */
            dest[i] = src[i];           /* fix, copy single run if present */
    }
    if (swap) {
        for (size_t i = 0; i < length; i++) {
            data[i] = buffer[i];
        }
    }
#else

替代修复:

    for (size_t step = 1; step < length; step *= 2, swap = !swap) {
        int* src = swap ? buffer : data;
        int* dest = swap ? data : buffer;
        for (size_t i = 0; i < length; i += (step * 2)) {                        /* fix */
            merge(src, dest, i, MIN(length, i+step), MIN(length, i+(step * 2))); /* fix */
        }

【讨论】:

  • 这绝对有效,尽管我不确定我在概念上理解为什么。也许是因为,由于数组并不总是 2 的幂,因此合并的大小不同,并且通过交替一些碎屑会留下一些碎片。无论如何,这解决了我的问题。谢谢并接受!
  • @WalterMays - 我添加了一个替代修复。 (使用 i
  • 我更喜欢替代修复。谢谢你的回答,真的很有帮助。
猜你喜欢
  • 1970-01-01
  • 2016-03-10
  • 1970-01-01
  • 1970-01-01
  • 2021-12-09
  • 2015-09-06
  • 1970-01-01
  • 2018-04-11
  • 1970-01-01
相关资源
最近更新 更多