优化选择排序?

时间:2016-02-26 03:52:12

标签: c algorithm sorting optimization selection-sort

我已阅读的消息来源说,选择排序的时间复杂性是:

  • 最佳案例:O(n ^ 2)
  • 平均情况:O(n ^ 2)
  • 最坏情况:O(n ^ 2)

我想知道它是否值得来优化"该算法通过添加一定代码行来使算法"短路"如果其余部分已经排序,则本身。

这里是用C:

编写的代码

我还添加了一条注释,指出哪些行是"优化"一部分。

void printList(int* num, int numElements) {
    int i;  

    for (i = 0; i < numElements; i ++) {
        printf("%d ", *(num + i));
    }
    printf("\n");
}

int main() {
    int numElements = 0, i = 0, j = 0, min = 0, swap = 0, numSorted = 0;

    printf("Enter number of elements: ");
    scanf("%d", &numElements);

    int* num = malloc(sizeof(int) * numElements);

    for (i = 0; i < numElements; i ++) {
        printf("Enter number = ");
        scanf(" %d", num + i);
    }

    for (i = 0; i < numElements-1; i++) {
        numSorted = i + 1;  // "optimized"
        min = i;

        for (j = i + 1; j < numElements; j++) {
            numSorted += *(num + j - 1) <= *(num + j);  // "optimized"
            if (*(num + min) > *(num + j))
                min = j;
        }

        if (numSorted == numElements)  // "optimized"
           break;

        if (min != i) {
            swap = *(num + i);
            *(num + i) = *(num + min);
            *(num + min) = swap;
        }

        printList(num, numElements);
    }

    printf("Sorted list:\n");
    printList(num, numElements);

    free(num);

    getch();
    return 0;

}

4 个答案:

答案 0 :(得分:2)

优化选择排序有点傻。它具有可怕的最佳情况,平均时间和最坏情况时间复杂度,因此如果您想要远程优化排序,您(几乎?)总是会选择另一种。即使插入排序往往更快,实现也不会复杂得多。

更重要的是,检查列表是否已排序会增加算法在最坏情况下所采用的时间(我也倾向于考虑平均情况)。即使是排序最多的列表也不一定会更快:考虑1,2,3,4,5,6,7,9,8。尽管列表最后只需要交换两个元素,但算法不会短路,因为它直到最后都没有排序。

答案 1 :(得分:0)

我看到如何解决这个问题的唯一方法是,如果您定义了为什么要对其进行优化,那么请参见。

  • 在专业环境中值得吗?在工作中,对于“在生产环境中”运行的代码-最可能(甚至几乎可以肯定),不是
  • 作为教学/学习工具值得吗?有时

我教个人编程,有时我教他们算法和数据结构。我认为选择排序是最容易解释和讲授的方法之一-在解释了找到最小值并交换两个值(swap())的算法后,它自然流动。然后,最后,我介绍了优化的概念,可以在其中实现此计数器“已排序”检测。

毫无疑问,冒泡排序最好引入优化,因为它至少具有3个易于解释和实质性的优化。

答案 2 :(得分:0)

仅仅是因为可以优化某些东西,不一定意味着它应该这样做。假设进行概要分析或“ boss-says-so”表明必须进行优化,那么您可以做一些事情。

与任何涉及内存迭代的算法一样,减少迭代次数的任何方法都可以提供帮助。

  • 保持最小和最大值的轨迹-将迭代次数减少一半
  • 保持跟踪多个最小/最大值(每个4个将是迭代的1/8)
    • 在某些时候,温度值无法放入寄存器中
    • 代码将变得更加复杂

它也可以帮助最大化缓存的位置。

  • 在正向迭代之后进行向后迭代
    • 最近访问的内存仍应缓存
    • 直接进入另一个前向迭代会导致缓存未命中
    • 由于您向后移动,缓存预测器可能会预取其余的
    • 在某些架构(RISC-V)上,这实际上可能更糟
  • 尽可能在高速缓存行上操作
    • 这可以允许在同一时间预取下一个缓存行
    • 您可能需要对齐数据或专门处理第一个和最后一个数据
      • 即使对齐方式有所提高,最后几个元素也可能需要“填充”

在有用且实用的地方使用SIMD指令和寄存器

  • 适用于非分支排名顺序的临时工
  • 可以同时保存许多数据点(AVX-512可以缓存行)
  • 避免内存访问(从而减少缓存丢失)

如果您使用多个最大值/最小值,请优化对最大值和最小值的n个值的排序

  • 有关对少量固定数量的值进行排序的技术,请参见here
  • 保存内存交换,直到每次迭代结束并执行一次
    • 同时保持寄存器中的临时变量(或指针)

还有很多可用的优化方法,但是最终与选择排序的相似性开始变得模糊。这些都会增加复杂性,从而增加维护成本,以使更合适的算法的更简单实现可能是更好的选择。

答案 3 :(得分:0)

  

我想知道是否有必要通过添加特定的代码行来“优化”算法,如果剩余部分已经被排序,则使算法本身“短路”。 / p>

显然,此更改将最佳情况下的复杂度从O(n 2 )降低到O(n)。对于除O(1)前导元素之外已经排序的输入,将观察到这一点。如果有这种可能,那么建议的代码更改实际上可能会带来可观且值得的性能改进。

但是请注意,您所做的更改使最内层循环中执行的工作增加了一倍以上,并考虑到对于统一的随机输入,预期的外层循环迭代次数为 1 。还要考虑一下,您设法修剪的任何外循环迭代将以其他方式执行的工作最少。总的来说,尽管您没有改变渐近复杂性,但平均情况和最坏情况下的实际性能会明显变差,运行时间是其两倍。

如果您追求更快的速度,那么最好的选择是选择其他排序算法。在比较排序中,插入排序与最佳选择排序在最佳情况下的性能大致相同,但是在最佳情况下的选择范围更广,通常在一般情况下胜过(常规)选择排序。两者在最坏的情况下如何比较取决于实现方式。

如果您仍然想要更好的性能,请考虑合并排序或快速排序,这两种方法都非常容易实现。或者,如果您的数据适合它,那么计数排序就很难被击败。