繁体   English   中英

为什么C快速排序功能比气泡排序功能慢得多(磁带比较,磁带交换)?

[英]Why is C quicksort function much slower (tape comparisons, tape swapping) than bubble sort function?

我将为学生实现一个玩具磁带“大型机”,显示“快速排序”类功能的快速性(递归与否,由于硬件速度慢,并且众所周知的堆栈反转技术并不重要) “bubblesort”函数类。 因此,虽然我清楚硬件实现和控制器,但我猜测,快速排序功能在顺序,顺序和比较距离方面要比其他功能快得多(从中间回放磁带要快得多)结束,因为倒带速度不同)。

不幸的是,事实并非如此; 这个简单的“气泡”代码在比较距离,方向和比较和写入次数方面与“快速排序”功能相比显示出很大的改进。

所以我有3个问题:

  1. 我实施快速排序功能时是否有错?
  2. 我在实现bubblesoft功能时遇到了错误吗?
  3. 如果没有,为什么“bubblesort”在(比较和写入操作)中的功能比“quicksort”功能快得多?

我已经有了“quicksort”功能:

void quicksort(float *a, long l, long r, const compare_function& compare)
{
    long i=l, j=r, temp, m=(l+r)/2;
    if (l == r) return;
    if (l == r-1)
    {
        if (compare(a, l, r))
        {
            swap(a, l, r);
        }
        return;
    }
    if (l < r-1)
    {
        while (1)
        {
            i = l;
            j = r;
            while (i < m && !compare(a, i, m)) i++;
            while (m < j && !compare(a, m, j)) j--;
            if (i >= j)
            {
                break;
            }
            swap(a, i, j);
        }
        if (l < m) quicksort(a, l, m, compare);
        if (m < r) quicksort(a, m, r, compare);
        return;
    }
}

我有自己的“bubblesort”功能的实现:

void bubblesort(float *a, long l, long r, const compare_function& compare)
{
    long i, j, k;
    if (l == r)
    {
        return;
    }
    if (l == r-1)
    {
        if (compare(a, l, r))
        {
            swap(a, l, r);
        }
        return;
    }
    if (l < r-1)
    {
        while(l < r)
        {
            i = l;
            j = l;
            while (i < r)
            {
                i++;
                if (!compare(a, j, i))
                {
                    continue;
                }
                j = i;
            }
            if (l < j)
            {
                swap(a, l, j);
            }
            l++;
            i = r;
            k = r;
            while(l < i)
            {
                i--;
                if (!compare(a, i, k))
                {
                    continue;
                }
                k = i;
            }
            if (k < r)
            {
                swap(a, k, r);
            }
            r--;
        }
        return;
    }
}

我在测试示例代码中使用了这些排序函数,如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <conio.h>

long swap_count;
long compare_count;

typedef long (*compare_function)(float *, long, long );
typedef void (*sort_function)(float *, long , long , const compare_function& );

void init(float *, long );
void print(float *, long );

void sort(float *, long, const sort_function& );
void swap(float *a, long l, long r);

long less(float *a, long l, long r);
long greater(float *a, long l, long r);

void bubblesort(float *, long , long , const compare_function& );
void quicksort(float *, long , long , const compare_function& );

void main()
{
    int n;
    printf("n=");

    scanf("%d",&n);
    printf("\r\n");

    long i;
    float *a = (float *)malloc(n*n*sizeof(float));

    sort(a, n, &bubblesort);
    print(a, n);

    sort(a, n, &quicksort);
    print(a, n);

    free(a);
}

long less(float *a, long l, long r)
{
    compare_count++;
    return *(a+l) < *(a+r) ? 1 : 0;
}

long greater(float *a, long l, long r)
{
    compare_count++;
    return *(a+l) > *(a+r) ? 1 : 0;
}

void swap(float *a, long l, long r)
{
    swap_count++;

    float temp;

    temp = *(a+l);
    *(a+l) = *(a+r);
    *(a+r) = temp;
}

float tg(float x)
{
    return tan(x);
}

float ctg(float x)
{
    return 1.0/tan(x);
}

void init(float *m,long n)
{
    long i,j;
    for (i = 0; i < n; i++)
    {
        for (j=0; j< n; j++)
        {
            m[i + j*n] = tg(0.2*(i+1)) + ctg(0.3*(j+1));
        }
    }
}

void print(float *m, long n)
{
    long i, j;
    for(i = 0; i < n; i++)
    {
        for(j = 0; j < n; j++)
        {
            printf("  %5.1f", m[i + j*n]);
        }
        printf("\r\n");
    }
    printf("\r\n");
}

void sort(float *a, long n, const sort_function& sort)
{
    long i, sort_compare = 0, sort_swap = 0;

    init(a,n);

    for(i = 0; i < n*n; i+=n)
    {
        if (fmod (i / n, 2) == 0)
        {
            compare_count = 0;

            swap_count = 0;
            sort(a, i, i+n-1, &less);

            if (swap_count == 0)
            {
                compare_count = 0;
                sort(a, i, i+n-1, &greater);
            }

            sort_compare += compare_count;
            sort_swap += swap_count;
        }
    }

    printf("compare=%ld\r\n", sort_compare);
    printf("swap=%ld\r\n", sort_swap);

    printf("\r\n");
}

我认为问题在于大多数快速排序实现依赖于分区步骤,该分区步骤在要排序的区域的相对端交替进行读取和写入。 在随机访问模型中,这非常好(所有读取基本上都是O(1)),但在磁带上这可能非常昂贵,因为在要排序的范围的不同端之间来回交换可能需要O( n)磁带卷轴一直向前和向后滚动的时间。 这通常将O(n)分区步骤转换为潜在的O(n 2 ),从而支配函数的运行时。 此外,由于进行磁带搜索所需的时间可能比处理器的频率慢数千或数百万倍,因此该O(n 2 )工作具有巨大的常数因子。

另一方面,冒泡排序没有这个问题,因为它总是比较阵列中的相邻单元格。 它最多可以在阵列上进行O(n)次通过,因此要求磁带只能重绕n次。 在冒泡排序中处理逻辑肯定更昂贵 - 比几乎任何其他O(n 2 )排序更加昂贵 - 但与没有来回寻找磁带所节省的时间相比,这没什么。

简而言之,快速排序在磁带上的运行速度应该比冒泡排序慢得多,因为它需要磁带在执行期间移动更多。 由于磁带搜索费用昂贵,因此快速排序的自然运行时优势将在此步骤中消耗掉,并且bubblesort应该看起来更快。

templatetypedef的答案是正确的钱。 bubbleort的访问不仅极少分散,而且可以就地运行 我怀疑它实际上是具有单个,任意慢速磁带和仅O(1)RAM的机器的最佳排序算法。 [编辑:实际上鸡尾酒排序 (bubbleort的双向版本)应该更好,因为它避免了浪费的倒带 - 感谢Steve Jessop。]

如果您有4个可用的磁带驱动器,那么mergesort可以控制它们 只有3个磁带,可以使用更高版本的mergesort

QuickSort比冒泡排序更快的原因之一是它可以瞬间移动元素很远的距离。 如果QuickSort将一个元素向上移动50个元素,然后向下移动20个,向上移动10个,向上移动5个向下移动2个向下移动到它的适当位置,该元素将从它开始的位置移动43个插槽,同时仅移动5次。 冒泡排序会使元素移动43次。 如果移动元素,则一个插槽的成本与将其移动50相同,这是一个重大胜利。 但是,如果移动元素的成本与距离成比例,则QuickSort将移动元素的总距离为87个插槽 - 是冒泡排序的两倍。

如果一个人处理磁带驱动器,最佳算法将在很大程度上取决于驱动器的物理工作方式。 例如,在某些驱动器上,唯一的操作是倒带并准备写入(有效擦除过程中的磁带),倒带并准备读取,并处理下一个字节(读取或写入,具体取决于倒带模式)。 其他驱动器允许在磁带上的任何位置随机访问和替换各个块。 有些驱动器仅限于向一个方向读取。 其他(例如QIC磁带)有一些轨道在一个方向读取而一些在另一个方向读取。 我不知道是否有任何驱动器允许在两个方向上读取或写入相同的数据块,但这样的事情至少在理论上是可能的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM