Sorting Golang自定义排序比本机排序快
我只是在golang玩排序,我在stackoverflow上发现了一个qsort函数。它的运行速度似乎是golang中本机排序函数的两倍。我尝试了不同的输入大小,并测试了它的工作原理 有人能解释为什么会发生这种情况吗 以下是您可以在pc上测试的代码:Sorting Golang自定义排序比本机排序快,sorting,go,native,qsort,Sorting,Go,Native,Qsort,我只是在golang玩排序,我在stackoverflow上发现了一个qsort函数。它的运行速度似乎是golang中本机排序函数的两倍。我尝试了不同的输入大小,并测试了它的工作原理 有人能解释为什么会发生这种情况吗 以下是您可以在pc上测试的代码: package main import ( "fmt" "math/rand" "sort" "time" ) func qsort(a []int) []int { if len(a) < 2 {
package main
import (
"fmt"
"math/rand"
"sort"
"time"
)
func qsort(a []int) []int {
if len(a) < 2 {
return a
}
left, right := 0, len(a)-1
// Pick a pivot
pivotIndex := rand.Int() % len(a)
// Move the pivot to the right
a[pivotIndex], a[right] = a[right], a[pivotIndex]
// Pile elements smaller than the pivot on the left
for i := range a {
if a[i] < a[right] {
a[i], a[left] = a[left], a[i]
left++
}
}
// Place the pivot after the last smaller element
a[left], a[right] = a[right], a[left]
// Go down the rabbit hole
qsort(a[:left])
qsort(a[left+1:])
return a
}
func main() {
// Create an array with random integers
rand.Seed(30)
size := 1000000
array1 := make([]int, size)
start := time.Now()
for i, _ := range array1 {
array1[i] = rand.Int()
}
fmt.Println("Creating array with ", size, " elements...")
fmt.Println("--- ", time.Since(start), " ---")
// Create a copy of the unsorted array
array2 := make([]int, size)
copy(array2, array1)
// Short using native function
start = time.Now()
sort.Ints(array1)
fmt.Println("Sorting with the native sort...")
fmt.Println("--- ", time.Since(start), " ---")
// Sort using custom qsort
start = time.Now()
qsort(array2)
fmt.Println("Sorting with custom qsort...")
fmt.Println("--- ", time.Since(start), " ---")
}
主程序包
进口(
“fmt”
“数学/兰德”
“排序”
“时间”
)
func qsort(a[]int)[]int{
如果len(a)<2{
归还
}
左,右:=0,len(a)-1
//选择一个支点
数据透视索引:=rand.Int()%len(a)
//将轴向右移动
[pivotIndex],a[right]=a[right],a[pivotIndex]
//在左侧堆积小于轴的图元
对于i:=范围a{
如果a[i]
差异似乎很大程度上是由于您的快速排序使用内置项。它切片并使用len
。请记住,sort.sort
接受一个sort.Interface
。因此,每次调用len
它都调用slice.len
并且每次调用array[i],array[j]=array[j],array[i]
它都必须调用Swap(i,j)
我编写了一个类似的版本,可以在任意qsort.Interface
上运行:
func Qsort(a Interface, prng *rand.Rand) Interface {
if a.Len() < 2 {
return a
}
left, right := 0, a.Len()-1
// Pick a pivot
pivotIndex := prng.Int() % a.Len()
// Move the pivot to the right
a.Swap(pivotIndex, right)
// Pile elements smaller than the pivot on the left
for i := 0; i < a.Len(); i++ {
if a.Less(i, right) {
a.Swap(i, left)
left++
}
}
// Place the pivot after the last smaller element
a.Swap(left, right)
// Go down the rabbit hole
leftSide, rightSide := a.Partition(left)
Qsort(leftSide, prng)
Qsort(rightSide, prng)
return a
}
qsort
的实际IntSlice
实现为:
type IntSlice []int
func (is IntSlice) Less(i, j int) bool {
return is[i] < is[j]
}
func (is IntSlice) Swap(i, j int) {
is[i], is[j] = is[j], is[i]
}
func (is IntSlice) Len() int {
return len(is)
}
func (is IntSlice) Partition(i int) (left Interface, right Interface) {
return IntSlice(is[:i]), IntSlice(is[i+1:])
}
结果(我的):
如您所见,对于随机数据,标准库的排序在平均性能上大大优于qsortNativeQsort
是指您在实际问题中发布的qsort
函数,它的性能优于这两个函数。在这和Qsort
之间唯一的变化是我将内置函数替换为Qsort.Interface
。因此,通用性很可能是一个比另一个慢的原因
编辑:由于排序非常昂贵,所以样本不多,因此这里是使用-benchtime 10s
的结果,只是为了获得更具代表性的结果
BenchmarkQsort 50 524389994 ns/op
BenchmarkNativeQsort 100 161199217 ns/op
BenchmarkSort 50 302037284 ns/op
内置使用qsort吗?qsort可以快一点,但也可以慢得令人难以置信(例如,对已经排序或几乎已经排序的数组进行排序时(这在实践中非常常见))。qsort最坏的情况是O(N^2),但对于许多其他类型,它是O(N logn)。有关Perl中的类似实验,请参阅。您应该在println之前计算时间增量,然后将其打印出来,因为println可能会干扰您的时间结果。我现在正在编写一个真正的基准测试,以尝试回答这个问题。首先让我印象深刻的是,
sort
使用sort.Interface
,因此必须在许多使用内置函数的地方调用方法。sort.sort不是mergesort的变体,而是快速排序/插入排序的组合。@Volker是这样的吗?我记得文件上说是合并排序。“完全有可能我弄错了。”沃尔克我一定是疯了。我刚看了资料来源,你是对的。我删除了答案中关于Mergesort的内容。看见稳定排序由mergesort完成。golang标准库使用Intro sort,这是一种混合排序,根据条件从快速排序切换到堆排序。
package qsort_test
import (
"math/rand"
"qsort"
"sort"
"testing"
"time"
)
const size int = 1000000
var list = make([]int, size)
var prng = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
func BenchmarkQsort(b *testing.B) {
for n := 0; n < b.N; n++ {
b.StopTimer()
for i := range list {
list[i] = prng.Int()
}
b.StartTimer()
qsort.Qsort(qsort.IntSlice(list), prng)
}
}
func BenchmarkNativeQsort(b *testing.B) {
for n := 0; n < b.N; n++ {
b.StopTimer()
for i := range list {
list[i] = prng.Int()
}
b.StartTimer()
qsort.NativeQsort(list, prng)
}
}
func BenchmarkSort(b *testing.B) {
for n := 0; n < b.N; n++ {
b.StopTimer()
for i := range list {
list[i] = prng.Int()
}
b.StartTimer()
sort.Sort(sort.IntSlice(list))
}
}
PASS
BenchmarkQsort 5 513629360 ns/op
BenchmarkNativeQsort 10 160609180 ns/op
BenchmarkSort 5 292416760 ns/op
BenchmarkQsort 50 524389994 ns/op
BenchmarkNativeQsort 100 161199217 ns/op
BenchmarkSort 50 302037284 ns/op