What is the correct way to find the min between two integers in Go?

Go

Go Problem Overview


I imported the math library in my program, and I was trying to find the minimum of three numbers in the following way:

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

where v1 is declared as:

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

However, when I run my program I get the following error:

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

I thought it was weird because I have another program where I write

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

and that program outputs 2 without complaining.

so I ended up casting the values as float64, so that math.Min could work:

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

With this approach, I got the following error:

./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

so to get rid of the problem, I just casted the result back to int

I thought this was extremely inefficient and hard to read:

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

I also wrote a small minInt function, but I think this should be unnecessary because the other programs that make use of math.Min work just fine when taking integers, so I concluded this has to be a problem of my program and not the library per se.

Is there anything that I'm doing terrible wrong?

Here's a program that you can use to reproduce the issues above, line 36 specifically: package main

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

Go Solutions


Solution 1 - Go

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.

Solution 2 - Go

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
}

Solution 3 - Go

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

Solution 4 - Go

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
}

Solution 5 - Go

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
}

Solution 6 - Go

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionILikeTacosView Question on Stackoverflow
Solution 1 - GotwotwotwoView Answer on Stackoverflow
Solution 2 - GoSergiu SandreanView Answer on Stackoverflow
Solution 3 - GopeterSOView Answer on Stackoverflow
Solution 4 - GoLoveRickView Answer on Stackoverflow
Solution 5 - GoleventovView Answer on Stackoverflow
Solution 6 - GoJoão WiciukView Answer on Stackoverflow