在 Go 中找到两个整数之间的最小值的正确方法是什么?

我在程序中导入了数学库,并试图通过以下方式找到至少三个数字:

v1[j+1] = math.Min(v1[j]+1, math.Min(v0[j+1]+1, v0[j]+cost))

V1声明为:

t := "stackoverflow"
v1 := make([]int, len(t)+1)

但是,当我运行我的程序时,我会得到以下错误:

./levenshtein_distance.go:36: cannot use int(v0[j + 1] + 1) (type int) as type float64 in argument to math.Min

我觉得很奇怪,因为我还有另一个程序

fmt.Println(math.Min(2,3))

程序输出 2没有抱怨。

所以我最终将值强制转换为 float64,这样 math.Min就可以工作:

v1[j+1] = math.Min(float64(v1[j]+1), math.Min(float64(v0[j+1]+1), float64(v0[j]+cost)))

通过这种方法,我得到了以下错误:

./levenshtein_distance.go:36: cannot use math.Min(int(v1[j] + 1), math.Min(int(v0[j + 1] + 1), int(v0[j] + cost))) (type float64) as type int in assignment

所以为了解决这个问题,我将结果转回到 int

我认为这是极其低效和难以阅读的:

v1[j+1] = int(math.Min(float64(v1[j]+1), math.Min(float64(v0[j+1]+1), float64(v0[j]+cost))))

我还编写了一个小的 minInt函数,但我认为这应该是不必要的,因为其他使用 math.Min的程序在获取整数时工作得很好,所以我得出结论,这一定是我的程序的问题,而不是库本身的问题。

我做错什么了吗?

这里有一个程序可以用来重现上面的问题,具体来说就是第36行: 主包裹

import (
"math"
)


func main() {
LevenshteinDistance("stackoverflow", "stackexchange")
}


func LevenshteinDistance(s string, t string) int {
if s == t {
return 0
}
if len(s) == 0 {
return len(t)
}
if len(t) == 0 {
return len(s)
}


v0 := make([]int, len(t)+1)
v1 := make([]int, len(t)+1)


for i := 0; i < len(v0); i++ {
v0[i] = i
}


for i := 0; i < len(s); i++ {
v1[0] = i + 1
for j := 0; j < len(t); j++ {
cost := 0
if s[i] != t[j] {
cost = 1
}
v1[j+1] = int(math.Min(float64(v1[j]+1), math.Min(float64(v0[j+1]+1), float64(v0[j]+cost))))
}


for j := 0; j < len(v0); j++ {
v0[j] = v1[j]
}
}
return v1[len(t)]
}
121871 次浏览

For example,

package main


import "fmt"


func min(x, y int) int {
if x < y {
return x
}
return y
}


func main() {
t := "stackoverflow"
v0 := make([]int, len(t)+1)
v1 := make([]int, len(t)+1)
cost := 1
j := 0


v1[j+1] = min(v1[j]+1, min(v0[j+1]+1, v0[j]+cost))


fmt.Println(v1[j+1])
}

Output:

1

Until Go 1.18 a one-off function was the standard way; for example, the stdlib's sort.go does it near the top of the file:

func min(a, b int) int {
if a < b {
return a
}
return b
}

You might still want or need to use this approach so your code works on Go versions below 1.18!

Starting with Go 1.18, you can write a generic min function which is just as efficient at run time as the hand-coded single-type version, but works with any type with < and > operators:

func min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}


func main() {
fmt.Println(min(1, 2))
fmt.Println(min(1.5, 2.5))
fmt.Println(min("Hello", "世界"))
}

There's been discussion of updating the stdlib to add generic versions of existing functions, but if that happens it won't be until a later version.

math.Min(2, 3) happened to work because numeric constants in Go are untyped. Beware of treating float64s as a universal number type in general, though, since integers above 2^53 will get rounded if converted to float64.

There is no built-in min or max function for integers, but it’s simple to write your own. Thanks to support for variadic functions we can even compare more integers with just one call:

func MinOf(vars ...int) int {
min := vars[0]


for _, i := range vars {
if min > i {
min = i
}
}


return min
}

Usage:

MinOf(3, 9, 6, 2)

Similarly here is the max function:

func MaxOf(vars ...int) int {
max := vars[0]


for _, i := range vars {
if max < i {
max = i
}
}


return max
}

Though the question is quite old, maybe my package imath can be helpful for someone who does not like reinventing a bicycle. There are few functions, finding minimal of two integers: ix.Min (for int), i8.Min (for int8), ux.Min (for uint) and so on. The package can be obtained with go get, imported in your project by URL and functions referred as typeabbreviation.FuncName, for example:

package main


import (
"fmt"
"<Full URL>/go-imath/ix"
)


func main() {
a, b := 45, -42
fmt.Println(ix.Min(a, b)) // Output: -42
}

Could use https://github.com/pkg/math:

import (
"fmt"
"github.com/pkg/math"
)


func main() {
a, b := 45, -42
fmt.Println(math.Min(a, b)) // Output: -42
}

If you want the minimum of a set of N integers you can use (assuming N > 0):

import "sort"


func min(set []int) int {
sort.Slice(set, func(i, j int) bool {
return set[i] < set[j]
})


return set[0]
}


Where the second argument to min function is your less function, that is, the function that decides when an element i of the passed slice is less than an element j

Check it out here in Go Playground: https://go.dev/play/p/lyQYlkwKrsA

As the accepted answer states, with the introduction of generics in go 1.18 it's now possible to write a generic function that provides min/max for different numeric types (there is not one built into the language). And with variadic arguments we can support comparing 2 elements or a longer list of elements.

func Min[T constraints.Ordered](args ...T) T {
min := args[0]
for _, x := range args {
if x < min {
min = x
}
}
return min
}


func Max[T constraints.Ordered](args ...T) T {
max := args[0]
for _, x := range args {
if x > max {
max = x
}
}
return max
}

example calls:

Max(1, 2) // 2
Max(4, 5, 3, 1, 2) // 5

Since the issue has already been resolved, I would like to add a few words. Always remember that the math package in Golang operates on float64. You can use type conversion to cast int into a float64. Keep in mind to account for type ranges. For example, you cannot fit a float64 into an int16 if the number exceeds the limit for int16 which is 32767. Last but not least, if you convert a float into an int in Golang, the decimal points get truncated without any rounding.