【问题标题】:My QuickSort code does not work for 1000000+ elements (one million elements or more)我的 QuickSort 代码不适用于 1000000 多个元素(一百万个元素或更多)
【发布时间】:2021-01-18 15:08:55
【问题描述】:

我尝试制作自己的排序算法(现在称为 MySort),并根据 QuickSort 的排序时间对其进行基准测试。我使用随机数生成器生成包含 n 个随机数的输入文件,然后将此文件作为 MySort 和 QuickSort 的输入提供,并使用 std::chrono 分别计算它们所花费的时间。

(起初我使用在线编译器来检查时间,但是当我达到输入 10000 个字符的限制时,我转而在我的 PC 上自己做。)

所以,对于最初的几次尝试(100 个元素、1000 个元素、10000 个元素、100000 个元素),一切正常。对于每个排序算法所花费的时间,我得到了适当的输出时间,但是当我尝试使用 1000000 个元素时,QuickSort 只是不提供任何输出(似乎根本不起作用),这很奇怪,因为 MySort 工作得很好。我不认为这是空间问题,因为 MySort 使用了 2n 额外空间并且工作正常。

我正在使用的 QuickSort 的实现如下:

#include <iostream>
#include <chrono>

using namespace std;
using namespace std::chrono;

void quick_sort(int[],int,int);
int partition(int[],int,int);

int main()
{
    int n,i;
    cin>>n;

    int a[n];

    for(i=0;i<n;i++)
        cin>>a[i];
    auto start = high_resolution_clock::now();        
    quick_sort(a,0,n-1);
    auto stop = high_resolution_clock::now();

    duration <double, micro> d = stop - start;
    cout<<"Time taken = "<<d.count()<<endl;
    /*
    cout<<"\nArray after sorting:";

    for(i=0;i<n;i++)
        cout<<a[i]<<endl;
    */
    return 0;        
}

void quick_sort(int a[],int l,int u)
{
    int j;
    if(l<u)
    {
        j=partition(a,l,u);
        quick_sort(a,l,j-1);
        quick_sort(a,j+1,u);
    }
}

int partition(int a[],int l,int u)
{
    int v,i,j,temp;
    v=a[l];
    i=l;
    j=u+1;

    do
    {
        do
            i++;

        while(a[i]<v&&i<=u);

        do
            j--;
        while(v<a[j]);

        if(i<j)
        {
            temp=a[i];
            a[i]=a[j];
            a[j]=temp;
        }
    }while(i<j);

    a[l]=a[j];
    a[j]=v;

    return(j);
}

我尝试四处寻找解决方案,以了解为什么它拒绝为一百万个元素工作,但除了可能是空间问题之外一无所获,考虑到 MySort 正在工作,这在我看来不太可能。

至于我在输入 1000000 个元素时得到的输出究竟是什么,当我在命令行上执行这两个文件时,我得到的输出是(都运行两次):

C:\Users\Zac\Desktop>MySortTest <output.txt                                      

Time Taken = 512129                                                                                                 

C:\Users\Zac\Desktop>MySortTest <output.txt                                      

Time Taken = 516131                                                                                                                                               

C:\Users\Zac\Desktop>QuickSortTest <output.txt                                                                                                                    

C:\Users\Zac\Desktop>QuickSortTest <output.txt                                                                                                                    

C:\Users\Zac\Desktop>    

但是,如果我只为 100000 个元素运行它们,这就是我得到的:

C:\Users\Zac\Desktop>MySortTest <output.txt                                      

Time Taken = 76897.1                                                                                                                                              

C:\Users\Zac\Desktop>MySortTest <output.txt                                      

Time Taken = 74019.4                                                                                                                                              

C:\Users\Zac\Desktop>QuickSortTest <output.txt                                   

Time taken = 16880.2                                                                                                                                              

C:\Users\Zac\Desktop>QuickSortTest <output.txt                                   

Time taken = 18005.3                                                                                                                                              

C:\Users\Zac\Desktop>  

似乎工作正常。

我无能为力,任何建议都会很棒。

【问题讨论】:

  • 可能是堆栈溢出。尝试使用std::vector&lt;int&gt; 而不是int[]。后者无论如何都不是便携式的。
  • int a[n]; -- 不要这样做。使用std::vector&lt;int&gt; a(n);。你被你使用的编译器愚弄,相信int a[n] 是有效的 C++ 代码。
  • Here is a link to your code failing to compile using Visual C++。错误如前面的 cmets 所述。另一位新程序员成为g++ 可变长度数组恶作剧的受害者。
  • 您的错误可能在这里:int a[n]; VLA 都不是 c++ 标准的一部分,并且大小有限。我希望这会导致堆栈溢出。
  • g++ 真的会在堆栈上分配 n == 100000 的 int a[n]; 吗?我不反对使用std::vector 的建议,只是想知道 VLA 是否真的是问题所在。

标签: c++ c++11 quicksort


【解决方案1】:
cin>>n;

int a[n];

这是你的错误。出于三个原因,您绝不应该这样做。

  1. 这不是有效的 C++。在 C++ 中,任何数组的维度都应该是一个常量表达式。你被不符合规范的 gcc 扩展愚弄了。您的代码将无法与其他编译器一起编译。您应该始终在高一致性模式下使用 gcc(和 clang)。对于 C++,它将是 g++ -std=c++17 -Wall -pedantic-errors
  2. 函数局部的大型数组可能会引发堆栈溢出,因为局部变量通常分配在堆栈上,而堆栈内存通常非常有限。
  3. C 样式数组不好,可以吗?它们不知道自己的大小,无法轻松检查越界访问(std::vectorstd::array 具有 at() 边界检查成员函数),并且无法将它们分配或传递给函数或从函数返回。请改用std::vector(或者在预先知道大小时使用std::array)。

【讨论】:

  • 这是对所有与此类似的 C++ VLA 问题的规范答案。我找不到一个好的彻底的骗局,所以我决定把它写下来。请根据需要进行编辑/改进。
  • 3.std::vectorstd::array 项中增加了at() 成员函数,以便对边界条件进行简单的完整性检查。 VLA 和数组通常没有这样的错误检查工具。
  • 刚刚发布了一个答案——代码有缺陷,OP 没有意识到这一点。
  • 谢谢。我使用向量再次编写了它,现在它工作正常。你们帮了大忙。
  • @Zac 我认为您仍然应该调查 PaulMcKenzie 提到的错误。
【解决方案2】:

让我们删除您正在使用的 VLA 并使用 std::vector。以下是包含 10 个项目的示例数据的代码(但需要检查边界条件)。

#include <iostream>
#include <chrono>
#include <vector>

using namespace std;
using namespace std::chrono;
using vint = std::vector<int>;
void quick_sort(vint&, int, int);
int partition(vint&, int, int);

int main()
{
    int n = 10, i;
    vint a = { 7, 43, 2, 1, 6, 34, 987, 23, 0, 6 };
    auto start = high_resolution_clock::now();
    quick_sort(a, 0, n - 1);
    auto stop = high_resolution_clock::now();
    duration <double, micro> d = stop - start;
    cout << "Time taken = " << d.count() << endl;
    return 0;
}

void quick_sort(vint& a, int l, int u)
{
    int j;
    if (l < u)
    {
        j = partition(a, l, u);
        quick_sort(a, l, j - 1);
        quick_sort(a, j + 1, u);
    }
}

int partition(vint& a, int l, int u)
{
    int v, i, j, temp;
    v = a[l];
    i = l;
    j = u + 1;

    do
    {
        do
           i++;
        while (a.at(i) < v&&i <= u);
        do
          j--;
        while (v < a[j]);
        if (i < j)
        {
            temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    } while (i < j);

    a[l] = a[j];
    a[j] = v;
    return(j);
}

Live Example

您会看到std::out_of_range 错误与std::vector.at() 调用有关。

底线——你的代码一开始就有缺陷——无论是 10、100 还是一百万个项目。您将越界,因此行为未定义。 std::vectorat() 的使用检测到错误,这是 VLA 不会给你的。

【讨论】:

    【解决方案3】:

    除了 VLA,您的快速排序始终选择枢轴作为第一个。这可能会导致它在最坏的情况下表现不佳。我不知道你的output.txt,但如果数组已经排序,它会运行O(n^2),因为每个分区都会分成一个元素,其余的(一半和一半是最好的)。我认为这就是为什么它不会为大输入提供任何输出。

    所以我会建议一些常用的选择枢轴的启发式方法。

    • 随机选择
    • 从 3 个元素中选择中位数 - 最低/中/最高索引 (a[l] / v[(l+u)/2] / v[u])

    一旦您选择了一个支点,您只需将其与v[lo] 交换即可,这样可以最大限度地减少您的代码更改。

    【讨论】:

      猜你喜欢
      • 2020-12-06
      • 2019-10-15
      • 1970-01-01
      • 1970-01-01
      • 2018-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多