快速排序速度明显变慢

Quick Sort significantly slower

提问人:underloaded_operator 提问时间:5/22/2023 更新时间:5/22/2023 访问量:97

问:

我正在做我的实验室作业,这是关于排序算法、 、 和 .我差不多完成了同化,但是在对每个算法进行时间测量后,我得到了令人惊讶的结果。Heap SortMerge SortQuick Sort

[***** [Merge Sort] *****]

[Original]: [54599, 62697, 92032, 19179, 17296, 27068, 99563, 9829, 89929, 57140]
[Sorted]:   [9829, 17296, 19179, 27068, 54599, 57140, 62697, 89929, 92032, 99563]


[size]:  10     [time]: 2      [ms]
[size]:  100    [time]: 15     [ms]
[size]:  1000   [time]: 170    [ms]
[size]:  10000  [time]: 2122   [ms]
[size]:  100000 [time]: 22946  [ms]

[***** [Quick Sort] *****]

[Original]: [10017, 37607, 51285, 83517, 7500, 81469, 40379, 19721, 48524, 74062]
[Sorted]:   [7500, 10017, 19721, 37607, 40379, 48524, 51285, 74062, 81469, 83517]


[size]:  10     [time]: 24     [ms]
[size]:  100    [time]: 95     [ms]
[size]:  1000   [time]: 1001   [ms]
[size]:  10000  [time]: 9697   [ms]
[size]:  100000 [time]: 107627 [ms]

[***** [Heap Sort] *****]

[Original]: [62697, 92032, 19179, 17296, 27068, 99563, 9829, 89929, 57140, 33429]
[Sorted]:   [9829, 17296, 19179, 27068, 33429, 57140, 62697, 89929, 92032, 99563]


[size]:  10     [time]: 1      [ms]
[size]:  100    [time]: 14     [ms]
[size]:  1000   [time]: 239    [ms]
[size]:  10000  [time]: 3088   [ms]
[size]:  100000 [time]: 39615  [ms]

我知道所有这些算法都应该运行,并且被认为是“最快”的排序算法,但时间测量与 和 有很大不同。O(nlogn)Quick SortHeap SortMerge Sort

我使用的是随机透视,因为我读到,如果所有元素都排序或所有元素都相同,则效率非常低。QS

这是我的QS代码:

/**
 * @brief Generates a random pivot index between low and high (inclusive)
 * @param low Starting index of the array
 * @param high Ending index of the array
 * @return Random pivot index
 */
int random_pivot(int low, int high) {
    srand(static_cast<unsigned int>(time(nullptr)));
    return low + rand() % (high - low + 1);
}

/**
 * @brief Partitions the array and returns the partition index
 * @param arr The array to be partitioned
 * @param low Starting index of the partition
 * @param high Ending index of the partition
 * @return Partition index
 */
int partition(int* arr, int low, int high) {
    int pivotIndex = random_pivot(low, high);
    int pivot = arr[pivotIndex];
    std::swap(arr[pivotIndex], arr[high]);

    int i = low - 1; // Index of the smaller element

    for (int j = low; j <= high - 1; j++) {
        // If current element is smaller than or equal to the pivot
        if (arr[j] <= pivot) {
            i++; // Increment index of smaller element
            std::swap(arr[i], arr[j]); // Swap current element with the smaller element
        }
    }

    std::swap(arr[i + 1], arr[high]); // Swap the pivot with the element at the partition index
    return i + 1; // Return the partition index
}

/**
 * @brief Sorts an array using the QuickSort algorithm
 * @param arr The array to be sorted
 * @param low Starting index of the array
 * @param high Ending index of the array
 */
void quick_sort_helper(int* arr, int low, int high) {
    if (low < high) {
        int partition_index = partition(arr, low, high); // partition the array and get the partition index
        quick_sort_helper(arr, low, partition_index - 1); // recursively sort the left subarray
        quick_sort_helper(arr, partition_index + 1, high); // recursively sort the right subarray
    }
}

/**
 * @brief Sorts an array using the QuickSort algorithm
 * @param arr The array to be sorted
 * @param size The size of the array
 */
void quick_sort(int* arr, int size) {
    quick_sort_helper(arr, 0, size - 1);
}

用于进行时间测量的代码块:

/**
 * @brief Measures the execution time of a sorting algorithm on arrays of different sizes.
 * @param sorting_function The sorting function to be measured.
 */
void measure_sort(void (*sorting_function)(int*, int)) {
  int sizes[] = {10, 100, 1000, 10000, 100000}; // sizes of the array
  int const MAX = 100000;
  int const SMALL = 10;

  for (auto i = 0; i < 5; i++) {
      int* arr = new int[sizes[i]];
      for(auto j = 0; j < sizes[i]; j++) { //fill array with random numbers
        arr[j] = rand() % MAX;
      }

      if (sizes[i] == SMALL) { //print og array before sorting
        std::cout << "\n[Original]: "; // << std::setw(2);
        print_arr(arr, sizes[i]);
      }

      // Measure execution time
      auto start = std::chrono::high_resolution_clock::now();
      sorting_function(arr, sizes[i]);
      auto end = std::chrono::high_resolution_clock::now();
      auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();

      if(sizes[i] == SMALL) {
        std::string const SPACE = "   "; //width const to align output
        std::cout << std::setw(4) << "[Sorted]:" << SPACE;
        print_arr(arr, sizes[i]);
        std::cout << std::endl << std::endl;
      }

      int const SIZE_W = 9;
      int const TIME_W = 8;
      int const W = 6;
      std::cout << std::left << std::setw(SIZE_W) << "[size]: " << std::setw(W+1) << sizes[i] << std::left <<std::setw(TIME_W) << "[time]: " << std::setw(W) << duration << " [ms]" << std::endl;

      // Clean up dynamically allocated memory
      delete[] arr;
  }
}

有人可以向我解释为什么对随机数组进行排序比其他算法花费更多的时间吗?QS

我回顾了这个问题这个但我仍然不明白发生了什么。

C++ 快速排序

评论

0赞 Eljay 5/22/2023
提供的代码不完整,无法编译。一个最小的可重复的例子将是最有帮助的。您是否尝试过使用调试器单步执行代码?您使用的是什么编译器开关?您的数据是否已排序,或者元素是否相同 - 如果没有,随机透视没有帮助。
0赞 Yksisarvinen 5/22/2023
单一测量很难争论。通常,您要进行多次测量并找到平均时间。话虽如此,我强烈怀疑.不仅调用可能很昂贵(因为它需要系统调用),而且您还会在每次调用时重置随机引擎,这意味着您的随机数可能远非随机。random_pivottime()
1赞 PaulMcKenzie 5/22/2023
另外,您不应该对所有 3 种分类使用相同的随机值集吗?
2赞 Davis Herring 5/22/2023
您不想调用每个随机数 - PRNG 的全部意义在于为它播种一次并获得随机序列。您甚至可能在同一秒内重复获得 0(分辨率为 )。srandtime
1赞 Davis Herring 5/22/2023
@PepijnKramer:适用于存储多个函数对象之一,尤其是带有捕获的 lambda。在这里使用函数指针是非常合适的,尽管在类似的情况下,如果每个指针有多个调用,你可能希望它们作为模板参数以提高效率。std::function

答:

3赞 RandomBits 5/22/2023 #1

调用和每个随机透视几乎肯定会影响实现的性能。相对于您正在执行的其他操作,这些调用的成本很高。srandtimequick_sort

您说得对,选择不当的透视点对于排序数据来说可能是灾难性的。但是,我建议选择枢轴的三个策略的中位数,而不是随机枢轴(另请参阅此答案)。

中位数 3 选择枢轴作为分区的第一个、中间和最后一个元素的中位数。这可以完美地处理排序数据,甚至可以通过平均更均匀地划分数据来提高随机数据的性能。

更新

您可能还想看看我对另一个与排序相关的问题的回答。可以在 GitHub 上找到关联的代码。

评论

0赞 Jesper Juhl 5/30/2023
更不用说 / 有一个可怕的周期和小范围,并且是 (P)RNG 的一个可怕的种子,因为它对于在同一秒内调用的任何程序都是相同的,并且很容易被外部攻击者猜测。srandrandtime(0)