查找最接近给定整数的Python dict的整数键

时间:2017-12-09 05:53:21

标签: python

我有一个字典,其中键是整数。我有另一个整数。我想找到与dict中最接近给定整数的键对应的值。有没有一种有效的方法呢?

也许不同的数据结构(二叉树)会更有效率?

5 个答案:

答案 0 :(得分:1)

dd = {2:'x', 6:'y', 100000:'z', 77:'w'}
ikey = 99
low = max([d for d in dd if d<= ikey])
high = min([d for d in dd if d>= ikey])
nearkey = low if ikey - low <= high - ikey else high
nearkey

77

答案 1 :(得分:1)

二进制搜索树

你对二叉搜索树的建议让我想到了如何实现它,所以我继续前进并做到了。如果我没有弄错的话,这会在查找期间提供O(log n)复杂度,但需要更多的设置时间(线性而不是常数),这意味着它只在更多读取密集的情况下才是最佳的。

from math import inf


class RangeMap:
    def __init__(self, dictionary, minimum=-inf, maximum=inf):
        assert dictionary
        self._dictionary = dictionary
        self.min, self.max = minimum, maximum
        self._generate_tree()

    def __delitem__(self, key):
        del self._dictionary[key]
        self._generate_tree()

    def __getitem__(self, item):
        current = self._tree
        while current:
            if item < current.lower:
                current = current.before
            elif item <= current.upper:
                return current.value
            else:
                current = current.after
        raise Exception("Tree not constructed properly")

    def __setitem__(self, key, value):
        self._dictionary[key] = value
        self._generate_tree()

    def _generate_tree(self):
        range_mapping = [(((p + c) / 2, (c + n) / 2), self._dictionary[c])
                         for p, c, n in self._iterate_keys()]
        self._tree = RangeMap.Node(range_mapping)

    def _iterate_keys(self):
        keys = sorted(self._dictionary.keys())
        return zip([self.min] + keys[:-1], keys, keys[1:] + [self.max])

    class Node:
        def __init__(self, range_mapping):
            assert range_mapping
            middle = len(range_mapping) // 2
            (self.lower, self.upper), self.value = range_mapping[middle]
            before, after = range_mapping[:middle], range_mapping[middle + 1:]
            self.before = RangeMap.Node(before) if before else None
            self.after = RangeMap.Node(after) if after else None

你会这样使用它(在距离上的关系[我完全达到的决定所以__getitem__不会打破无限 - 将这个改变__getitem__中的<=改为<] ):

>>> dictionary = {
...     4: 'four',
...     8: 'eight',
...     15: 'fifteen',
...     16: 'sixteen',
...     23: 'twenty-three',
...     42: 'forty-two'
... }
>>> range_map = RangeMap(dictionary)
>>> range_map[11]
'eight'
>>> range_map[12]
'fifteen'
>>> range_map[15]
'fifteen'
>>> range_map[16]
'sixteen'
>>> range_map[19]
'sixteen'

我兄弟的想法

我问我的非程序员兄弟他将如何解决这个问题,他想出了从你想要的钥匙向外检查(我把它放到代码中)。

def approximate(dictionary, key):
    assert dictionary and all(isinstance(k, int) for k in dictionary)
    i = 0
    while True:
        if key + i in dictionary:
            return dictionary[key + i]
        if key - i in dictionary:
            return dictionary[key - i]
        i += 1

我以为我会包含这个,因为如果您正在查找的整数总是接近字典的键,这可能是一个很好的解决方案。

答案 2 :(得分:0)

最好你要做的就是O(n)运行时。

def closestKey(dic, key):
    diff = {k:abs(k - key) for k in dic}
    return min(diff, key=diff.get)

答案 3 :(得分:0)

除了迭代整个字典,然后通过记录min_diff以及将密钥本身作为变量查看最接近哪个密钥,我没有看到任何其他方法。

或者,您可以尝试使用有序的dict来节省一些时间,但不管怎样,它都会以线性时间运行。

答案 4 :(得分:0)

In [1]: def lookforkey(mykey, dd):
     ...:     """ returns mykey if present in dd
     ...:         otherwise the nearest key
     ...:         which could be either greater or less
     ...:         than mykey
     ...:     """
     ...:     nearkey = next(iter(dd))
     ...:     bestdist = abs(nearkey - mykey)
     ...:     for ikey in dd:
     ...:             dist = abs(mykey - ikey)
     ...:             if bestdist > dist:
     ...:                 bestdist = dist
     ...:                 nearkey = ikey
     ...:                 if ikey == mykey:
     ...:                     return ikey
     ...:     return nearkey
     ...:  

In [2]: dd = {2: 'x', 6: 'y', 77: 'w', 100000: 'z'}
In [3]: lookforkey(-10, dd)
Out[3]: 2
In [4]: lookforkey(6, dd)
Out[4]: 6
In [5]: lookforkey(76, dd)
Out[5]: 77
In [6]: lookforkey(999, dd)
Out[7]: 77
In [8]: lookforkey(999999, dd)
Out[9]: 100000
即使它更长,这比仍然处理密钥两次的最小/最大解决方案更有效。在这里,如果有完全匹配,则无需继续查看