Go二分查找错误

huangapple go评论94阅读模式
英文:

Go binary search error

问题

假设你有两个使用案例:

a := []int{2, 2, 3, 4}
i := sort.Search(len(a), func(pos int) bool { return a[pos] == 2 })
fmt.Printf("%v -> %v \n", a, i)

b := []int{1, 2, 2, 3, 4}
j := sort.Search(len(b), func(pos int) bool { return b[pos] == 2 })
fmt.Printf("%v -> %v \n", b, j)

这两个案例的答案是:

[2 2 3 4] -> 4
[1 2 2 3 4] -> 1

我认为在这两种情况下都应该是 1,是吗?有人知道为什么吗?

英文:

Suppose you have two use cases:

a := [] int {2, 2, 3, 4}
i := sort.Search(len(a), func(pos int) bool { return a[pos] == 2})
fmt.Printf("%v -> %v \n", a, i)

b := [] int {1, 2, 2, 3, 4}
j := sort.Search(len(b), func(pos int) bool { return b[pos] == 2})
fmt.Printf("%v -> %v \n", b, j)

Answer for those are:

[2 2 3 4] -> 4
[1 2 2 3 4] -> 1

I suppose it must be 1 in both cases, no? Does anyone knows why?

答案1

得分: 4

sort.Search() 函数返回在 [0, n) 范围内满足 f(i)true 的最小索引 i

由于切片索引是从 0 开始的,所以你应该期望第一个返回值为 0,第二个返回值为 1。第二个例子(看起来)是正确的。

引用文档中的说明:

> ...假设在范围 [0, n) 上,f(i) == true 意味着 f(i+1) == true。也就是说,Search 要求 f 在输入范围 [0, n) 的某个(可能为空)前缀上为 false,然后在剩余部分(可能为空)上为 trueSearch 返回第一个为 true 的索引。如果没有这样的索引,Search 返回 n

你的函数不符合 sort.Search() 的要求。你的函数不是用来判断指定索引处的元素是否是你要找的元素,而是用来判断:

  • 如果请求的索引处的元素大于或等于搜索的元素,则返回 true
  • 如果请求的索引处的元素小于搜索的元素,则返回 false

(如果你只告诉它们相等而不告诉它们大于或小于,就无法进行二分查找。)

所以只需使用 >= 比较而不是 ==。正确的用法如下:

a := []int{2, 2, 3, 4}
i := sort.Search(len(a), func(pos int) bool { return a[pos] >= 2 })
fmt.Printf("%v -> %v \n", a, i)

b := []int{1, 2, 2, 3, 4}
j := sort.Search(len(b), func(pos int) bool { return b[pos] >= 2 })
fmt.Printf("%v -> %v \n", b, j)

输出结果(在 Go Playground 上尝试):

[2 2 3 4] -> 0 
[1 2 2 3 4] -> 1 

注意:文档中还提到:

> 例如,给定一个按升序排序的切片数据,调用

>

> Search(len(data), func(i int) bool { return data[i] >= 23 })
>

> 返回最小的索引 i,使得 data[i] >= 23。如果调用者想要找到 23 是否在切片中,它必须单独测试 data[i] == 23

更简单的替代方法:sort.SearchInts()

如果你想在 int 切片([]int)中搜索一个 int 值,可以直接使用 sort.SearchInts()

a := []int{2, 2, 3, 4}
i := sort.SearchInts(a, 2)
fmt.Printf("%v -> %v \n", a, i)

b := []int{1, 2, 2, 3, 4}
j := sort.SearchInts(b, 2)
fmt.Printf("%v -> %v \n", b, j)

输出结果(在 Go Playground 上尝试):

[2 2 3 4] -> 0 
[1 2 2 3 4] -> 1 
英文:

sort.Search() returns the smallest index i in [0, n) at which f(i) is true.

Since slice indices are 0-based, you should expect a return of 0 for the first and 1 for the second. It (seemingly) works for the second.

Quoting from the doc:

> ...assuming that on the range [0, n), f(i) == true implies f(i+1) == true. That is, Search requires that f is false for some (possibly empty) prefix of the input range [0, n) and then true for the (possibly empty) remainder; Search returns the first true index. If there is no such index, Search returns n.

Your function does not meet the criteria sort.Search() expects. Your function is not to tell if the element at the specified index is the one you're looking for, but rather to tell:

  • if the element at the requested index is equal to or is greater than (return true)
  • or if the element at the specified index is less than the searched one (return false).

(A binary search could not be carried out if you would only tell when they are equal but not when they are greater or less.)

So simply use >= comparison instead of ==. Correct usage:

a := []int{2, 2, 3, 4}
i := sort.Search(len(a), func(pos int) bool { return a[pos] >= 2 })
fmt.Printf("%v -> %v \n", a, i)

b := []int{1, 2, 2, 3, 4}
j := sort.Search(len(b), func(pos int) bool { return b[pos] >= 2 })
fmt.Printf("%v -> %v \n", b, j)

Output (try it on the <kbd>Go Playground</kbd>):

[2 2 3 4] -&gt; 0 
[1 2 2 3 4] -&gt; 1 

Note: also from the doc:

> For instance, given a slice data sorted in ascending order, the call
>
> Search(len(data), func(i int) bool { return data[i] >= 23 })
>
> returns the smallest index i such that data[i] &gt;= 23. If the caller wants to find whether 23 is in the slice, it must test data[i] == 23 separately.

Easier alternative: sort.SearchInts()

If you want to search for an int value in an int slice ([]int), simply use sort.SearchInts():

a := []int{2, 2, 3, 4}
i := sort.SearchInts(a, 2)
fmt.Printf(&quot;%v -&gt; %v \n&quot;, a, i)

b := []int{1, 2, 2, 3, 4}
j := sort.SearchInts(b, 2)
fmt.Printf(&quot;%v -&gt; %v \n&quot;, b, j)

Output (try it on the <kbd>Go Playground</kbd>):

[2 2 3 4] -&gt; 0 
[1 2 2 3 4] -&gt; 1 

huangapple
  • 本文由 发表于 2015年11月30日 21:32:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/33999844.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定