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

but5z9lq  于 2023-06-03  发布在  Go
关注(0)|答案(8)|浏览(210)

我在我的程序中导入了数学库,我试图通过以下方式找到三个数字中的最小值:
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)]
}
gfttwv5a

gfttwv5a1#

在Go 1.18之前,一次性函数是标准方法;例如,the stdlib's sort.go在文件顶部附近执行:

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

您可能仍然希望或需要使用这种方法,以便您的代码可以在1.18以下的Go版本上运行!
从Go 1.18开始,你可以写一个generic min function,它在运行时和手工编码的单类型版本一样高效,但是可以使用<>操作符处理任何类型:

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", "世界"))
}

已经有discussion的更新stdlib来添加现有函数的泛型版本,但如果发生这种情况,则要等到更高版本。
math.Min(2, 3)碰巧工作了,因为numeric constants in Go are untyped。不过,一般要注意将float 64视为通用数字类型,因为如果转换为float 64,则大于2^53的整数将被舍入。

qyuhtwio

qyuhtwio2#

对于整数没有内置的min或max函数,但编写自己的函数很简单。由于支持可变参数函数,我们甚至可以通过一次调用来比较更多的整数:

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

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

    return min
}

用途:

MinOf(3, 9, 6, 2)

下面是max函数:

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

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

    return max
}
aurhwmvo

aurhwmvo3#

比如说

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

输出:

blmhpbnm

blmhpbnm4#

正如公认的答案所述,随着go1.18中泛型的引入,现在可以编写一个泛型函数,为不同的数值类型提供min/max(语言中没有内置)。使用可变参数,我们可以支持比较2个元素或更长的元素列表。

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
}

示例调用:

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

kyxcudwk5#

虽然这个问题已经很老了,但也许我的imath包对那些不喜欢重新发明自行车的人有所帮助。有几个函数,找到两个整数的最小值:ix.Min(对于int)、i8.Min(对于int8)、ux.Min(对于uint)等等。该软件包可以通过go get获得,通过URL和引用为typeabbreviation.FuncName的函数导入到项目中,例如:

package main

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

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

j13ufse26#

可以使用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
}
fkaflof6

fkaflof67#

如果你想要一组N个整数中的最小值,你可以使用(假设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]
}

其中,min函数的第二个参数是less函数,即决定传入切片的元素i何时小于元素j的函数
在Go Playground中查看:https://go.dev/play/p/lyQYlkwKrsA

dluptydi

dluptydi8#

issue 59488之后,Go 1.21 (Q3 2023)将包括两个新的内置命令,如CL 498495所示:

// The max built-in function returns the largest value of a fixed number of
// arguments of [cmp.Ordered] types. There must be at least one argument.
func max[T cmp.Ordered](x T, y ...T) T
// The min built-in function returns the smallest value of a fixed number of
// arguments of [cmp.Ordered] types. There must be at least one argument.
func min[T cmp.Ordered](x T, y ...T) T

如下所述:

最小和最大

内置函数minmax分别计算固定数量的有序类型参数的最小值或最大值。
必须至少有一个参数。
适用与运算符相同的类型规则:

  • 对于有序参数xy,如果x + y有效,则min(x, y)有效,并且min(x, y)的类型是x + y的类型(对于max也是如此)。
  • 如果所有参数都是常量,则结果是常量。
var x, y int
m := min(x)                 // m == x
m := min(x, y)              // m is the smaller of x and y
m := max(x, y, 10)          // m is the larger of x and y but at least 10
c := max(1, 2.0, 10)        // c == 10.0 (floating-point kind)
f := max(0, float32(x))     // type of f is float32
var s []string
_ = min(s...)               // invalid: slice arguments are not permitted
t := max("", "foo", "bar")  // t == "foo" (string kind)

对于数值参数,min和max是可交换的和关联的:

min(x, y)    == min(y, x)
min(x, y, z) == min(min(x, y), z) == min(x, min(y, z))

相关问题