字典中的递归查找

时间:2016-02-29 18:48:06

标签: python

我想为稀疏数据做一种字典类。这个想法是,如果键不在字典中,找到具有较低值的最接近的键(我的字典的键总是正整数,最低值始终为零)

这是我的原型

class specialdict(dict):
    def __getitem__(self, beg):
        try:
            return self[beg - 1]
        except:
            return dict.__getitem__(self, beg)

a = specialdict([(10, True)])

print a[137]

print a[500]

这样可行,但最多只能达到338

我想它与递归有关,但我虽然在python中的递归数量更高。我还需要查找速度非常快......

我做错了什么,有更好的方法吗?

感谢

编辑:

一个例子:

如果我只有钥匙" 0"," 10"和" 15",并搜索关键" 13",我希望 getitem 函数给我对应 getitem (10)

如果我想要钥匙" 100"应该得到 getitem (15))的值。

编辑2

我没有特别需要将其作为字典,或 getitem 函数进行递归。但我觉得这将是最快的方式。

编辑3

我尝试了@gilland @ thomas-lotze提出的所有3种解决方案:

import bisect

class SparseData(object):

    def __init__(self, pairs=()):
        if pairs:
            indexes, values = zip(*pairs)
            self.indexes = list(indexes)
            self.values = list(values)
        else:
            self.indexes = []
            self.values = []


    def __setitem__(self, index, value):
        i = bisect.bisect(self.indexes, index)
        self.indexes.insert(i, index)
        self.values.insert(i, value)

    def __getitem__(self, index):
        i = bisect.bisect(self.indexes, index)
        if not i:
            raise IndexError(i)
        return self.values[i-1]

class specialdict_rec(dict):
    def __getitem__(self, beg):
        try:
            return dict.__getitem__(self, beg)
        except KeyError:
            return self[beg - 1]

class specialdict_non_rec(dict):
    def __getitem__(self, beg):
        while beg >= 0:
            try:
                return dict.__getitem__(self, beg)
            except KeyError:
                beg -= 1

这里是基准的结果:

In [1]: a = [(1, '1'), (7, '7'), (100, '100')]

In [2]: a1 = SparseData(a)

In [3]: a2 = specialdict_rec(a)

In [4]: a3 = specialdict_non_rec(a)

In [5]: %timeit -n10000 a1[200]
10000 loops, best of 3: 1.12 µs per loop

In [6]: %timeit -n10000 a2[200]
10000 loops, best of 3: 96 µs per loop

In [7]: %timeit -n10000 a3[200]
10000 loops, best of 3: 58.6 µs per loop

因此,递归并没有改善任何事情,正如@gill所说,这很危险。

但最后我要使用的解决方案是来自@ thomas-lotze的解决方案。 非常感谢你的答案!

2 个答案:

答案 0 :(得分:3)

这将实现您的狭隘目标(假设密钥为int并且不会小于0),但就像@JustinR在评论中所说的那样,可能会有更好的解决方案问题

class specialdict(dict):
    def __getitem__(self, beg):
        while beg >= 0:
            try:
                return dict.__getitem__(self, beg)
            except KeyError:
                beg -= 1

修改

只是为了展示如何递归地执行相同的操作(因为OP问),但这是非常不推荐的。有递归限制。正如其他人所说,二元搜索更有效率。

class specialdict(dict):
    def __getitem__(self, beg):
        try:
            return dict.__getitem__(self, beg)
        except KeyError:
            return self[beg - 1]

编辑2

改进@ ThomasLotze的答案,以下是在保持bisect界面时如何包装dict

import bisect

class SpecialDict(dict):

    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
        self._keys = sorted(self.keys())  # maintain a sorted list of keys

    def __setitem__(self, key, value):
        if key not in self:
            bisect.insort(self._keys, key)
        dict.__setitem__(self, key, value)

    def __getitem__(self, key):
        if key not in self:
            try:
                key = self._keys[bisect.bisect(self._keys, key) - 1]
            except IndexError:
                raise KeyError(key)
        return dict.__getitem__(self, key)

答案 1 :(得分:1)

对于速度,使用the bisect module处理(键,值)对列表而不是字典:

import bisect

class SparseData(object):

    def __init__(self, pairs=()):
        if pairs:
            indexes, values = zip(*pairs)
            self.indexes = list(indexes)
            self.values = list(values)
        else:
            self.indexes = []
            self.values = []

    def __setitem__(self, index, value):
        i = bisect.bisect(self.indexes, index)
        if self.indexes[i-1] == index:
            self.values[i-1] = value
        else:
            self.indexes.insert(i, index)
            self.values.insert(i, value)

    def __getitem__(self, index):
        i = bisect.bisect(self.indexes, index)
        if not i:
            raise IndexError(i)
        return self.values[i-1]

>>> x = SparseData([(1, '1'), (2, '2'), (4, '4')])

>>> x[0]
Traceback (most recent call last):
...
IndexError: 0

>>> x[3]
'2'
>>> x[4]
'4'

>>> x[27] = '27'
>>> x[25]
'4'
>>> x[27]
'27'
>>> x[29]
'27'