算法分析(2):归并排序——发现了一个wiki上的BUG

  归并排序的核心思想是将已经排好序的A[p~q],和A[q~r](其中A为数组,p、q、r为数组下标)合并为单一已排序的数组A[p~r]。实现的代码如下:

void _Merge(int* input,int* output, int low, int mid, int high)
{
    int i = low;
    int j = mid;
    while (i < mid && j < high)
    {
        output[low++] = input[i] <= input[j] ? input[i++] : input[j++];
    }
    copy(&input[i], &input[mid], &output[low]);
    copy(&input[j], &input[high], &output[low]);
}

  但是想要排列一个数组,还需要将数组分解。我采用的是在wiki上面的方法,但是发现有错误。先上我经过修改的代码:

void Merge(int* a, int n)
{
    int *copy_a = a; 
    int *b = new int[n];
    int start = 0;
    for (int seg = 1; seg < n; seg += seg)
    {
        for (start = 0; start <= n - seg; start += 2*seg)
        {
            _Merge_ascending(a, b, start, start + seg, min(start + 2 * seg, n));
        }
        if (start < n)
        {
            copy(&a[start], &a[n], &b[start]);
        }
        swap(a, b);
    }
    if (a != copy_a) {
        copy(a, &a[n], b);
        b = a;
    }
    delete[] b;
}

  上面的代码完成的过程为:先将所有元素两两合并,输入到数组b,然后a,b互换;在四四合并,并以此类推,直到所有元素被排序。

  下面上,wiki的代码:

void merge_sort(int arr[], int len) {
    int* a = arr;
    int* b = new int[len];
    for (int seg = 1; seg < len; seg += seg) {
        for (int start = 0; start + seg <= len; start += seg + seg)
            _merge_sort(a, b, start, start + seg, min(start + seg + seg, len));
        swap(a, b);
    }
    if (a != arr) {
        copy(a, &a[len], b);
        b = a;
    }
    delete[] b;
}

  可以看到,区别在完成一轮的合并后,wiki的代码没有将没有进行合并的元素输出到b数组。

  为此,我还测试了wiki的代码。测试方法为:数组元素个数从1~1000变化,每个元素使用随机数。每次将上节的插入排序和wiki的代码作比较,如果不同则记录下此次的数组长度。

    vector<clock_t> dt1;
    vector<clock_t> dt2;
    vector<int> seq;
    vector<int> error_count;
    vector<int> error_wiki;
    for (int i = 1; i < MAX; i++)
    {
        seq.clear();
        for (int j = 0; j < i; j++)
        {
            seq.push_back(rand()%1000);
        }
        //Show_array(arr, i);
        int *arr1 = new int[i];
        int *arr2 = new int[i];
        int *arr3 = new int[i];
        V2A(seq, arr1);
        V2A(seq, arr2);
        V2A(seq, arr3);

        //ascending
        auto t1 = GetCycleCount();
        Insert_ascending(arr1, i);
        auto t2 = GetCycleCount();
        Merge_ascending(arr2, i);
        auto t3 = GetCycleCount();
        Merge_ascending_wiki(arr3, i);

        //Compare
        if (!Compare_array(arr1, arr2, i))
            error_count.push_back(i);
        else
            if (!Compare_array(arr1, arr3, i))
                error_wiki.push_back(i);

        //decending
        auto t4 = GetCycleCount();
        Insert_decending(arr1, i);
        auto t5 = GetCycleCount();
        Merge_decending(arr2, i);
        auto t6 = GetCycleCount();
        dt1.push_back(((t2 - t1)+(t5-t4))/2);
        dt2.push_back(((t3 - t2)+(t6-t5))/2);
        Merge_decending_wiki(arr3, i);

        //Compare
        if (!Compare_array(arr1, arr2, i))
            error_count.push_back(-i);
        else
            if (!Compare_array(arr1, arr3, i))
                error_wiki.push_back(-i);

    }
    cout << "Error Count:" << error_count.size() << endl;
    for (auto &it : error_count)
        cout << it << "\t";
    cout << endl;

    cout << "Wiki Error Count:" << error_wiki.size() << endl;
    for (auto &it : error_wiki)
        cout << it << "\t";
    cout << endl;

  上面的代码还增加了计算插入排序和归并排序在不同元素个数下排序所用时间的比较。获取CPU时间的函数为:

inline unsigned __int64 GetCycleCount()
{
    __asm _emit 0x0F
    __asm _emit 0x31
}

  比较结果为

posted @ 2015-07-04 11:06  uniqueS  阅读(397)  评论(0编辑  收藏  举报