我想要解决的问题可以这样表达:我想在整数范围的hashmap中查找Integer。
0-4: dog,
5-8: cat,
9-18: bird,
19-21: dog,
22-22: bird,
...
其中:
lookup(3) -> dog
lookup(10) -> bird
然而,将此问题视为hashmap可能不是正确的方法。 我正在使用约140,000个范围,这些范围属于~200个可能类别中的一个。
想知道如何在Golang中这样做吗?或者通过哪条跟踪来达到合理的解决方案(~O(log(n)?)?更一般地描述这个问题的方法是什么?
感谢您的帮助!
答案 0 :(得分:5)
如果范围 disjunct (具体数字只能属于一个范围),您可以使用二进制搜索找到范围。这是O(log(n))
复杂性。
如果范围是连续的,那么仅使用一个数字“开始”或“结束”来“建模”范围也就足够了。这也适用于您的情况。
我们可以按照上升顺序列出int
切片中的范围边界,我们可以在此排序切片中执行二进制搜索。我们用最大值对范围进行建模,因为范围序列没有任何孔。这将为我们提供范围的索引。我们可以将名称存储在一个单独的切片中,并在我们刚刚找到的索引处返回名称作为二进制搜索的结果。
这是一个令人惊讶的短暂实现,单行功能:
var ranges = []int{-1, 4, 8, 18, 21, 22}
var names = []string{"", "dog", "cat", "bird", "dog", "bird", ""}
func getName(n int) string {
return names[sort.SearchInts(ranges, n)]
}
测试它:
nums := []int{-1, 3, 6, 10, 20, 22, 100}
for _, n := range nums {
if name := getName(n); name == "" {
fmt.Printf("Invalid number: %4d\n", n)
} else {
fmt.Printf("Number : %4d, Name: %s\n", n, name)
}
}
输出(在Go Playground上尝试):
Invalid number: -1
Number : 3, Name: dog
Number : 6, Name: cat
Number : 10, Name: bird
Number : 20, Name: dog
Number : 22, Name: bird
Invalid number: 100
注意:此解决方案也用于Code Review StackExchange网站上的类似问题:Classifying by age
如果范围不会覆盖每个数字(意味着范围之间有“洞”),那么您可以通过将孔添加为“虚拟”范围来轻松处理,并为它们提供空字符""
名称(我们用于无效范围)。就是这样。
例如,让我们将原始问题修改为:
0-4: dog,
5-8: cat,
9-15: bird,
19-21: dog,
22-22: bird,
如您所见,9-15: bird
和19-21:dog
之间存在“漏洞”。范围16-17
无效。以下是您可以如何映射:
var ranges = []int{-1, 4, 8, 15, 18, 21, 22}
var names = []string{"", "dog", "cat", "bird", "", "dog", "bird", ""}
""
和15
之间的范围有一个空的18
名称。测试它:
nums := []int{15, 16, 19}
for _, n := range nums {
if name := getName(n); name == "" {
fmt.Printf("Invalid number: %4d\n", n)
} else {
fmt.Printf("Number : %4d, Name: %s\n", n, name)
}
}
输出(在Go Playground上尝试此变体):
Number : 15, Name: bird
Invalid number: 16
Number : 19, Name: dog
答案 1 :(得分:4)
实现sort.Interface
而不是使用2个切片并处理非连续范围的稍微不同的方法:
type Range struct {
Min, Max int
Value string
}
type Ranges []Range
func (r Ranges) Len() int { return len(r) }
func (r Ranges) Less(i, j int) bool { return r[i].Min < r[j].Min }
func (r Ranges) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r Ranges) Sort() { sort.Sort(r) }
func (r Ranges) Search(v int) string {
ln := r.Len()
if i := sort.Search(ln, func(i int) bool { return v <= r[i].Max }); i < ln {
if it := &r[i]; v >= it.Min && v <= it.Max {
return it.Value
}
}
return ""
}