[ GO-pdqsort v1.0-实现与性能对比| 青训营笔记 ]

185 阅读2分钟

1. pdqsort

pdqsort (pattern-defeating-quicksort)是一种不稳定的混合排序算法,它的不同版本被应用在C++ BOOST、Rust 以及Go 1.19中。它对常见的序列类型做了特殊的优化,使得在不同条件下都拥有不错的性能。

2. pdqsort V1.0

image.png

  • 综合了快速排序、堆排序、直接插入排序三种排序算法的优缺点。
  • 快速排序在面对一般乱序的序列时有最好的排序性能,因此首先使用快速排序处理输入序列。
  • 直接插入排序在面对短序列和有序性时有最优的排序性能,因此当当前处理序列长度小于等于24时使用直接插入排序处理。本版本使用序列首元素作为pivot。
  • 堆排序在面对大部分分布的序列时都能保持稳定的排序性能。因此当快速排序表现不佳时,转而使用堆排序保证排序的最坏性能。
  • 当快速选择后的位置离序列两端很接近时(距离小于length/8)判定快速排序一次表现不佳,当这种情况的次数达到 limit (即bits.Len(length))时,切换到堆排序。

3. pdqsort V1.0 实现

func sort(nums []int, start int, end int, limit int) {  
    if end-start+1 < 24 {  
        InsertSort(nums, start, end)  
    } else if limit == 0 {  
        HeapSort(nums, start, end)  
    } else {  
        position := getPosition(nums, start, end)  
        if (position-start) < (end-start+1)/8 || (end-position) < (end-start+1)/8 {  
            limit--  
        }  
        sort(nums, start, position-1, limit)  
        sort(nums, position+1, end, limit)  
    }  
}  
  
func pdqSort(nums []int) {  
    limit := bits.Len(uint(len(nums)))  
    sort(nums, 0, len(nums)-1, limit)  
}

4. 几种排序算法的性能对比

  • 选择3种不同的序列分布:有序(sorted),随机(random),元素重复度较高(mod8)
  • 选择3种不同的序列长度:16,128,1024
  • 使用Go Benchmark工具测试快速排序、堆排序、直接插入排序、pdqSort的排序性能。
  1. 序列长度为16的情况:

image.png 2. 序列长度为128的情况:

image.png 3. 序列长度为1024的情况:

image.png

短序列情况下,直接插入排序在3种不同序列分布下都有最优的排序效率。

快速排序能够处理大部分数据分布的中长序列。堆排序的排序性能非常稳定,排序时间只与序列长度相关。

pdqSort在短序列时使用了直接插入排序,保证了短序列的排序性能。排序主体使用了快速排序,在快速排序能够有较好性能时,pdqSort速度较快速排序略好。而当快速排序无法适应序列分布时,又能够获得与堆排序相当的性能。