要在Go中找到一个分片的中位数,你需要写一个自定义函数,对输入的分片进行排序并得到中间的元素。如果片断大小是偶数,该函数会计算两个中间值的平均值(平均数)。在下面的例子中,我们将展示这个函数的两个版本:一个是常规的,以float64 slice为输入,另一个是通用版本,可以适用于任何数字类型。
在GO中计算中位数 - 示例
要以经典的方式计算中位数,我们需要创建一个新的median() 函数,并从输入数据副本开始。计算中位数需要先对数据进行排序,所以为了避免修改原始分片顺序,我们需要一个输入分片的副本。然后,我们从这个排序后的片断中取出中间值。这就是我们的中位数。如果数据大小是一个奇数,中间元素将是索引<slice_length>/2 的值。如果数据大小是偶数,它将是两个中间元素的算术平均值。
package main
import (
"fmt"
"sort"
)
func median(data []float64) float64 {
dataCopy := make([]float64, len(data))
copy(dataCopy, data)
sort.Float64s(dataCopy)
var median float64
l := len(dataCopy)
if l == 0 {
return 0
} else if l%2 == 0 {
median = (dataCopy[l/2-1] + dataCopy[l/2]) / 2
} else {
median = dataCopy[l/2]
}
return median
}
func main() {
fmt.Println(median([]float64{1, 3, 2}))
fmt.Println(median([]float64{1}))
fmt.Println(median([]float64{3, 3, 3, 3, 2, 22, 2, 2, 2, 2, 1, 1, 1, 1, 1, 111}))
}
输出
2
1
2
使用泛型来计算中位数
自从Go 1.18中的泛型函数发布后,我们可以编写median() ,该函数可以适用于任何数字类型的片断。只需定义Number 约束,这是一个由 constraints.Float和 constraints.Integer来限制输入类型为数字。更多的细节,请看如何使用泛型计算算术平均值的例子,在那里我们也使用了Number 约束。
另一个区别是,在通用版本中,我们使用了 slices.Sort()函数从新的 /x/exp/slices包中的函数来对片断进行排序。它允许对任何类型的片子进行通用排序。
package main
import (
"fmt"
"golang.org/x/exp/constraints"
"golang.org/x/exp/slices"
)
type Number interface {
constraints.Float | constraints.Integer
}
func median[T Number](data []T) float64 {
dataCopy := make([]T, len(data))
copy(dataCopy, data)
slices.Sort(dataCopy)
var median float64
l := len(dataCopy)
if l == 0 {
return 0
} else if l%2 == 0 {
median = float64((dataCopy[l/2-1] + dataCopy[l/2]) / 2.0)
} else {
median = float64(dataCopy[l/2])
}
return median
}
func main() {
fmt.Println(median([]float64{1, 3, 2}))
fmt.Println(median([]int{1}))
fmt.Println(median([]int32{3, 3, 3, 3, 2, 22, 2, 2, 2, 2, 1, 1, 1, 1, 1, 111}))
}
结果是一个函数可以无缝地适用于int,int64,float64 等类型的片断,而不必复制代码,或为每种类型创建特殊的版本。
输出
2
1
2