【问题标题】:Hash a Range of Values散列一系列值
【发布时间】:2012-02-21 00:36:25
【问题描述】:

我知道我可以散列奇异值作为 dict 中的键。例如,我可以将5 散列为dict 中的键之一。

我目前面临一个问题,需要我对一系列值进行散列。

基本上,我需要一种更快的方法来做到这一点:

if 0 <= x <= 0.1:
    # f(A)
elif 0.1 <= x <= 0.2:
    # f(B)
elif 0.2 <= x <= 0.3:
    # f(C)
elif 0.3 <= x <= 0.4:
    # f(D)
elif 0.4 <= x <= 0.5:
    # f(E)
elif 0.5 <= x <= 0.6:
    # f(F)

其中x 是一些任意精度的float 参数。

我能想到的最快的方法是散列,但问题是:我可以使用 (0.1, 0.2) 作为键,但这仍然会花费我 O(n) 的运行时间,并且最终不会比elifs(我必须遍历键并检查是否有 key[0] &lt;= x &lt;= key[1])。

有没有办法对一系列值进行散列,以便我可以检查0.15 的散列表并仍然得到#execute B

如果这样的散列是不可能的,我还能如何改进它的运行时间?我正在处理足够大的数据集,线性运行时间不够快。

编辑:为了回应奇恩的回答,我必须注意不能假定间隔是规则的。事实上,我几乎可以保证他们不是

为了响应 cmets 中的请求,我应该提到我这样做是为了尝试实现 fitness-based selection in a genetic algorithm。算法本身是做功课的,但具体实现只是为了提高生成实验数据的运行时间。

【问题讨论】:

  • 请注意,在第一个 if 之后的 0.n &lt;= 部分后一个 elifs 在逻辑上是多余的。这将使代码更快。
  • 你有大量的区间吗?
  • 您能否详细解释一下您在做什么以及为什么?我很难相信这里的分支速度占主导地位,除非 A 到 F 几乎什么都不做,在这种情况下我会有一组不同的建议。
  • 所有范围放在一起时是否保证是连续的,或者可以是.1 &lt; x &lt; .2,然后跳到.3 &lt; x &lt; .4.2 &lt; x &lt; .3 什么都没有?
  • 如果你真的关心速度,我会考虑用 c 或汇编编写那部分代码,然后用 python 引用它

标签: python hash universal-hashing


【解决方案1】:

正如其他人所指出的,您将获得的最佳算法是 O(log N),而不是 O(1),类似于通过排序列表进行二等分搜索。

在 Python 中执行此操作的最简单方法是使用 bisect 标准模块 http://docs.python.org/library/bisect.html。请特别注意第 8.5.2 节中的示例,关于进行数字表查找——这正是您正在做的事情:

>>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
...     i = bisect(breakpoints, score)
...     return grades[i]
...
>>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
['F', 'A', 'C', 'C', 'B', 'A', 'A']

grades 字符串替换为函数列表,将breakpoints 列表替换为您的阈值下限列表,然后就可以了。

【讨论】:

  • +1 给所有建议的人。我接受您对bisect 的描述性示例的回答
【解决方案2】:

您不一定需要散列整个值范围。例如,在上面给出的比例中,如果给定的是 0.15,则可以将其四舍五入到 0.2(小数点后的第一个数字),然后散列 0.2。

这必须有多高效?您可以尝试的另一种方法是二进制搜索。将区间值按排序顺序存储在列表中,并对其进行二进制搜索。例如:

sorted_list = [ (0.1, function1), (0.2, function2), ....(0.6, function6) ] 

然后您只需进行二分搜索即可找到大于 x 的最小元素。这将产生 O(log(n))。

【讨论】:

  • 我喜欢 O(log n) 方法。如果事实证明没有 O(1) 解决方案,那么这就是我将采用的方法。
  • 您还可以将二叉树与如果间隔是规则的该怎么办的想法结合起来 - 将范围划分为大小为 0.1(甚至 0.01?)的常规 bin,然后在每个 bin 存储中跨越该 bin 的间隔列表。对于大的 n,它仍然是 O(log n),但乘法常数要低很多。
  • @inspectorG4dget:确实没有 O(1) 解决方案。
  • @Moses,正是我在用 C 编写的基于适应度的选择算法中所做的。这种方法平均为 O(区间数)+ O(1)*(搜索数),特别是如果得分的数字是随机抽取的。在最坏的情况下,它是 O(Number of interval) + O(log N) * (Number of search)。
  • @inspectorG4dget:我认为应该开始用 O(log n) 来识别 O(1)。甚至散列也取决于你散列的长度(即 log_n 有多少这样大小的东西)
【解决方案3】:

如果您的间隔是规则的,您可以缩放然后floor 您的操作数到每个范围的最小值,然后将该结果直接传递到 dict 将这些下限映射到适当的处理程序。

使用您提供的范围的示例实现。

# Integerize our 0.1 width intervals; scale by x10
handlerDict = {}
handlerDict[0] = lambda x: ... # 0.1
handlerDict[1] = lambda x: ... # 0.2
handlerDict[2] = lambda x: ... # 0.3
...

# Get the right handler, scaling x by x10; handle
handlerDict[int(10*x)](x, ...)

【讨论】:

  • 间隔绝对不规则。我已编辑问题以反映这一点
  • @inspectorG4dget 它们至少有一些非无穷小的公分母,还是几乎是随机的?
  • 我只能假设几乎是随机的。我正在尝试在遗传算法中实现基于适应度的选择
【解决方案4】:

为了提高运行时间,您可以实现二等分搜索。

否则,您可以将区间阈值放在 trie 上。

编辑: 让我提出一个实现:

class IntervalHash():
    def __init__(self,SortedList):
        #check it's sorted 
        self.MyList = []
        self.MyList.extend(SortedList) 
        self.lenlist = len(self.MyList)
    def get_interval(self,a):
        mylen = self.lenlist 
        mypos = 0
        while mylen > 1:
            mylen = (mylen/2 + mylen % 2)
            if mypos + mylen > self.lenlist - 1:
                if self.MyList[self.lenlist - 1] < a:
                    mypos = self.lenlist - 1
                break
            if self.MyList[mypos + mylen] < a:
                mypos += mylen
        if mypos == 0:
            if self.MyList[0] > a: 
                return ("-infty",self.MyList[0],0)
        if mypos == self.lenlist - 1:
            return (self.MyList[mypos],"infty",0)
        return (self.MyList[mypos],self.MyList[mypos+1],0)

A = [0.32,0.70,1.13]
MyHasher = IntervalHash(A)
print "Intervals are:",A
print 0.9 ," is in ",MyHasher.get_interval(0.9)
print 0.1 ," is in ",MyHasher.get_interval(0.1)
print 1.8 ," is in ",MyHasher.get_interval(1.8)

欢迎进一步编辑和改进! trie 方法涉及更多,我认为它更适合低级语言。

【讨论】:

  • 我喜欢 O(log n) 方法。如果事实证明没有 O(1) 解决方案,那么这就是我将采用的方法。
  • 某种树结构几乎肯定比排序列表好,是的。 (但是,我不认为 trie 是一个特别好的树选择——你能详细说明它是如何工作的吗?)
  • 优秀的例子补充;赞成!我同意你的观点,这更适合低级语言(而 Python 实际上已经将其作为内置模块。)
猜你喜欢
  • 1970-01-01
  • 2014-05-19
  • 2016-09-04
  • 2012-01-17
  • 2014-07-27
  • 2018-12-07
  • 2013-07-13
  • 2011-07-03
  • 1970-01-01
相关资源
最近更新 更多