加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

排序 – Golang自定义排序比本机排序更快

发布时间:2020-12-16 19:04:06 所属栏目:大数据 来源:网络整理
导读:我只是在golang中进行排序,我在stackoverflow上找到了一个qsort函数.它的运行速度似乎是golang中本机排序函数的两倍.我尝试过不同的输入尺寸并测试它的工作原理. 谁能解释为什么会这样? 以下是您可以在PC上测试的代码: package mainimport ( "fmt" "math/r
我只是在golang中进行排序,我在stackoverflow上找到了一个qsort函数.它的运行速度似乎是golang中本机排序函数的两倍.我尝试过不同的输入尺寸并测试它的工作原理.

谁能解释为什么会这样?

以下是您可以在PC上测试的代码:

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[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("--- "," ---")

    // Sort using custom qsort
    start = time.Now()
    qsort(array2)

    fmt.Println("Sorting with custom qsort...")
    fmt.Println("--- "," ---")

}
差异似乎主要是因为您的Quicksort使用内置函数.它切片并使用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,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
}

然后我使用了Go’s benchmark functionality(你应该尽可能使用基准测试).

对于参考和透明度,qsort.Interface定义为:

type Interface interface {
    sort.Interface
    // Partition returns slice[:i] and slice[i+1:]
    // These should references the original memory
    // since this does an in-place sort
    Partition(i int) (left Interface,right Interface)
}

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:])
}

最后,这是qsort_test.go文件:

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

正如您所看到的,标准库的排序在随机数据上平均优于您的qsort. NativeQsort是指您在实际问题中发布的qsort函数,它优于两者.在Qsort和Qsort之间唯一改变的是我交换了qsort.Interface的内置函数.因此,通用性可能是一个比另一个慢的原因.

编辑:由于排序的成本很高,所以样本数量不多,所以这里的结果是-benchtime 10s只是为了更具代表性的结果.

BenchmarkQsort                50     524389994 ns/op
BenchmarkNativeQsort         100     161199217 ns/op
BenchmarkSort                 50     302037284 ns/op

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读