【问题标题】:"Fuzzy" hashtables in Python to replace elif chains?Python中的“模糊”哈希表替换elif链?
【发布时间】:2014-05-25 16:58:02
【问题描述】:

选择依赖于变量的许多执行路径或数据位之一的 Pythonic 方法是使用哈希表,如下所示:

mapping = {'bar':"xyzzy", 'foo':"plugh", 'baz':"frobnitz"}
magic = mapping[command]

如果我想根据一个值是否在多个值范围之一内来选择执行路径,我该怎么办?

或者,更简洁地说,我如何将其 Python 化:

    #A value from one to four indicates an O-type star. 4/58 chance.
    if typeselec <= 4:
        self.specttype="O"
    #A value from 5-20 indicates a B-type star. 15/58 chance.
    elif typeselec <= 20:
        self.specttype="B"
    #A value from 20-30 indicates an A-type star. 10/58 chance.
    elif typeselec <= 30:
        self.specttype="A"
    #A value from 31-36 indicates an F-type star. 6/58 chance.
    elif typeselec <= 36:
        self.specttype="F"
    #A value from 37-40 indicates a G-type star. 4/58 chance.
    elif typeselec <= 40:
        self.specttype="G"
    #A value from 41-50 indicates a K-type star. 10/58 chance.
    elif typeselec <= 22:
        self.specttype="K"
    #A value from 50-58 indicates an M-type star. 8/58 chance.
    else:
        self.specttype="M"

一个明显的方法是{1:'O', 2:'O',3:'O',4:'O',5:'B',6:'B'...},但这似乎非常不优雅和低效。有没有其他办法?

【问题讨论】:

  • 您可以在__getitem__ 中使用您想要的逻辑构建您自己的FuzzyMap
  • 我可以,那会看起来不错,但最终它仍然是一个 elif 链,虽然是一个隐藏的 elif 链。我想知道是否可以保留 Python 的非模糊映射的 O(1) 性能。
  • O(1) 来自哈希的使用,因此唯一的其他选择是自定义键对象,其中每个范围内的所有值都哈希到相同的值。
  • 是的,我也是这么认为的,但我怀疑这是可能的。但我以前错了。
  • 您能否详细说明为什么您认为将计算值缓存在散列中并不优雅?顺便说一句,如果您的值是连续的,您可以考虑使用列表

标签: python-3.x hashmap simplify


【解决方案1】:

正是这种类型或问题被用作bisect 模块的示例。

适应你的问题,它看起来像这样:

from bisect import bisect_left

def select_type(typeselec,
                breakpoints=[4, 20, 30, 36, 40, 50],
                types='0BAFGKM'):
    return types[bisect_left(breakpoints, typeselec)]


for i in range(59):
    print(i, select_type(i))

与文档中示例的不同之处在于,此处的断点是下限 (&lt;=) 的一部分,而在示例中它是上限 (&gt;=) 的一部分,因此 bisect_left 需要代替bisectbisect_right 的别名)。

虽然bisect 没有O(1) 的时间复杂度,它使用了二分查找,所以它的时间复杂度为O(log(n)),对于较大的n 将是@987654332 的一个很好的改进@ 的if 级联。

【讨论】:

    【解决方案2】:

    将您的specttypes 存储在加权列表中,并以typeselec 作为索引查找它们。生成列表后,查找时间为 O(1)。

    letters = 'OBAFGKM'
    frequencies = (4, 15, 10, 6, 4, 10, 8)
    
    specttypes = []
    for i in range(len(letters)):
        l = letters[i]
        f = frequencies[i]
        specttypes = specttypes + [l] * f
    
    self.specttype = specttypes[typeselec-1] # Subtract 1 because of zero indexing.
    

    您可以使用字典而不是单独的迭代 lettersfrequencies,但除非您使用 ordered dictionary,否则生成的 specttypes 将按字母顺序排列。

    请注意,specttypes 有上面定义的 57 个元素,而不是您提到的 58 个。那是因为您最后的代码注释自相矛盾;我为“M”分配了 8 而不是 9 的频率(,索引 50-57 而不是 50-58)。

    【讨论】:

    • 我喜欢这种效率,但我将不得不使用bisect 答案以获得更优雅的效果。
    【解决方案3】:

    如果我们有大量范围,我会将数字存储在排序序列中,并使用bisect 进行二分搜索;查找是 O(log(n)) 而不是 O(n)。仍然不是完美散列的 O(1)。当然,使用您展示的非常有限的集合,您不妨将值存储在单个字符串中并将其用作普通查找表。 chikinn 的示例这样做,但使用了一个列表(想想看,它会更有效率 - 无需查找字符的字符串版本)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-15
      • 2012-05-06
      相关资源
      最近更新 更多