C++ 快速排序奇怪的时间复杂度,c++;

C++ 快速排序奇怪的时间复杂度,c++;,c++,algorithm,performance,time-complexity,quicksort,C++,Algorithm,Performance,Time Complexity,Quicksort,我一直在测试不同数字序列的不同排序算法的时间复杂度,直到我得到quicksort(中间是pivot)的结果,其中一个是升序,另一个是降序。图表: (我所说的“V”是指前半部分下降,另一半上升的序列,而“a”是指前半部分上升,另一半下降的序列。) 其他类型序列的结果与我预期的一样,但我的算法可能有问题 void quicksort(int l,int p,int *tab) { int i=l,j=p,x=tab[(l+p)/2],w; //x - pivot do { while (

我一直在测试不同数字序列的不同排序算法的时间复杂度,直到我得到quicksort(中间是pivot)的结果,其中一个是升序,另一个是降序。图表:

(我所说的“V”是指前半部分下降,另一半上升的序列,而“a”是指前半部分上升,另一半下降的序列。)

其他类型序列的结果与我预期的一样,但我的算法可能有问题

void quicksort(int l,int p,int *tab)
{
int i=l,j=p,x=tab[(l+p)/2],w; //x - pivot
do 
{
    while (tab[i]<x)
    {
        i++;
    }
    while (x<tab[j])
    {
        j--;
    }
    if (i<=j)
    {
        w=tab[i];
        tab[i]=tab[j];
        tab[j]=w;
        i++;
        j--;
    }
}
while (i<=j);
if (l<j)
{
    quicksort(l,j,tab);
}
if (i<p)
{
    quicksort(i,p,tab);
}
}
void快速排序(int l、int p、int*tab)
{
int i=l,j=p,x=tab[(l+p)/2],w;//x-pivot
做
{

而(tab[i]快速排序最坏情况下的时间复杂度为O(n^2),数据集中n个条目的平均时间复杂度为O(n log n)。有关时间复杂度分析的更多详细信息,请参见:

在这里:


在快速排序中,影响运行时间的主要因素之一是使输入为ramdom

一般来说,在特定位置选择轴可能不是最好的,除非输入是随机乱序的。使用三个分区的
中间值是一种广泛使用的方法,只是为了确保轴是一个随机数。从您的代码中,您没有实现它

此外,当递归快速排序使用内部堆栈时,会遇到一些开销(必须生成多个函数并分配参数),因此,建议在剩下的数据大小大约为
10-20
时,可以使用其他排序算法,如
InsertionSort
,因为这将使排序速度加快约
20%

void quicksort(int l,int p,int *tab){
  if ( tab.size <= 10 ){

      IntersionSort(tab);
   }
 ..
 ..}
void快速排序(int l、int p、int*tab){

如果(tab.size这里的所有答案都有非常好的点。我的想法是,算法没有任何错误(因为枢轴问题是众所周知的,它是O(n^2)的原因),但是你测量它的方式有一些错误

clock()
-返回从某个点经过的处理器计时数(可能是程序启动?不重要)

你测量时间的方式依赖于滴答声的恒定长度,我认为这并不能保证

问题是,那么多(全部?)现代处理器动态地改变频率以节省能源。我认为,这是非常不确定的,所以每次启动程序时,CPU频率不仅取决于输入的大小,还取决于当前系统中发生的情况。我的理解是,一个刻度的长度可能会非常不同在程序执行期间

我试着查找,宏
每秒时钟数实际上是什么。它是当前每秒时钟数吗?它在某个神秘的时间段内做了一些平均值吗?我一直无法找到。因此,我认为你测量时间的方法可能是完全错误的

由于我的论点是站在某一点上的,我不确定,我可能完全错了

一种方法是在不同的总体系统使用情况下,使用相同的数据多次运行多个测试,查看每次的行为是否有显著差异。另一种方法是,将计算机的CPU频率设置为某个静态值,并以类似的方式进行测试

想法用滴答声来衡量“时间”不是更好吗

编辑1感谢@BeyelerStudios,现在我们可以肯定地知道,您不应该依赖Windows机器上的
clock()
,因为它不符合C98标准


我希望我能帮忙,如果我错了,请纠正我-我是一名学生,而不是硬件专家。

TL;博士:问题在于轴心选择策略,在这些类型的输入(a型和V型序列)上反复做出错误的选择。这导致快速排序高度“不平衡”递归调用,这反过来会导致算法性能非常差(A型序列的二次时间)

祝贺您,您(重新)发现了选择中间元素作为轴心的quicksort版本的敌对输入(或者更确切地说是一系列输入)

作为参考,A形序列的一个示例是
123432 1
,即增加的序列,到达中间的拾取点,然后减少;V形序列的一个示例是
43234
,即减少的序列,在中间达到最小值,然后增加

想想当你选择中间元素作为A形或V形序列的轴心时会发生什么。在第一种情况下,当你将算法传递给A形序列
12…n-1 n-1…2 1
时,轴心是数组中最大的元素——这是因为A形序列中最大的元素是中间元素,而u选择中间元素作为轴心---然后对大小为
0
的子数组进行递归调用(您的代码实际上没有对
0
元素和
n-1
进行调用)。在下一次对大小为
n-1
的子数组进行调用时,您将选择子数组中最大的元素作为轴心(它是原始数组的第二大元素);依此类推。这会导致性能不佳,因为运行时间是O(n)+O(n-1)+…+O(1)=O(n^2),因为在每一步中,基本上都要传递几乎整个数组(除了枢轴以外的所有元素)换句话说,递归调用中数组的大小是高度不平衡的

这是A形序列的轨迹
123454321

blazs@blazs:/tmp$ ./test 
pivot=5
   1   2   3   4   1   4   3   2   5
pivot=4
   1   2   3   2   1   3   4   4
pivot=3
   1   2   3   2   1   3
pivot=3
   1   2   1   2   3
pivot=2
   1   2   1   2
pivot=2
   1   1   2
pivot=1
   1   1
pivot=4
   4   4
   1   1   2   2   3   3   4   4   5
从跟踪中可以看出,在递归调用时,算法选择了一个最大的元素(最多可以有两个最大的元素,因此文章a而不是the)作为轴心。这意味着a形序列的运行时间实际上是O(n)+O(n-1)+…+O(1)=O(n^2)。(在技术术语中,a形序列是
1 2 3 ... n-1 n n-1 ... 3 2 1
double seconds(size_t n) {
    int *tab = (int *)malloc(sizeof(int) * (2*n - 1));
    size_t i;

    // construct A-shaped sequence 1 2 3 ... n-1 n n-1 ... 3 2 1
    for (i = 0; i < n-1; i++) {
        tab[i] = tab[2*n-i-2] = i+1;
        // To generate V-shaped sequence, use tab[i]=tab[2*n-i-2]=n-i+1;
    }
    tab[n-1] = n;
    // For V-shaped sequence use tab[n-1] = 1;

    clock_t start = clock();
    quicksort(0, 2*n-2, tab);
    clock_t finish = clock();

    free(tab);

    return (double) (finish - start) / CLOCKS_PER_SEC;
}
#include <stdio.h>

void print(int *a, size_t l, size_t r);
void quicksort(int l,int p,int *tab);

int main() {
    int tab[] = {1,2,3,4,5,4,3,2,1};
    size_t sz = sizeof(tab) / sizeof(int);

    quicksort(0, sz-1, tab);
    print(tab, 0, sz-1);

    return 0;
}


void print(int *a, size_t l, size_t r) {
    size_t i;
    for (i = l; i <= r; ++i) {
        printf("%4d", a[i]);
    }
    printf("\n");
}

void quicksort(int l,int p,int *tab)
{
int i=l,j=p,x=tab[(l+p)/2],w; //x - pivot
printf("pivot=%d\n", x);
do 
{
    while (tab[i]<x)
    {
        i++;
    }
    while (x<tab[j])
    {
        j--;
    }
    if (i<=j)
    {
        w=tab[i];
        tab[i]=tab[j];
        tab[j]=w;
        i++;
        j--;
    }
}
while (i<=j);

print(tab, l, p);
if (l<j)
{
    quicksort(l,j,tab);
}
if (i<p)
{
    quicksort(i,p,tab);
}
}