如何用python保持范围?

时间:2014-09-10 13:57:36

标签: python python-2.7 range

我应该保持范围(具有不同的间隔)和如下的值:

  • 0..5000:1234
  • 5001..10000:1231
  • 10001..20000:3242
  • ...
  • 50001..100000:3543
  • 100001..200000:2303
  • ...

我该如何储存?像{'0': 1234, '5001': 1231, '10001': 3242, ...}这样的词组?

存储后,我需要搜索相应值,但它应该查找范围 - 例如,6000应该返回1231。如果我将其存储为dict,我该如何执行搜索?

UPD。间隔中没有间隙,范围数量非常小(~50)。

6 个答案:

答案 0 :(得分:3)

我建议您将其存储为词典列表,因为:

Explicit is better than implicit.

>>> rang = [{'start': 0, 'end': 5000, 'id': 1234}, {'start': 5000, 'end': 10000, 'id': 1231}, {'start': 10001, 'end': 20000, 'id': 342}]
>>> num = 10
>>> for r in rang:
...   if r['start'] < num < r['end']:
...     print r['id']
... 
1234
>>> num = 10500
>>> for r in rang:
...   if r['start'] < num < r['end']:
...     print r['id']
... 
342
>>> 

答案 1 :(得分:3)

您可以将其定义为dict,如下所示:

d = {"0:5000": {"range": [0, 5000],
                "value": 1234},
     "5001:10000":{"range":[5001, 10000],
                   "value": 1432}}

但我认为课程更合适

class MyRange(object):

    def __init__(self, start, end, value):
        self.start = start
        self.end = end
        self.value = value

    def has_in_range(self, num):
        return self.start <= num <= self.end

然后,您可以获得MyRange元素列表

l = [MyRange(0, 5000, 1234), MyRange(5001, 10000, 3124)]

最后,当您想要搜索时,请使用其他功能

def search(num):
    for element in l:
        if element.has_in_range(num):
            return element.value
    return -1    

以便搜索返回:

>>> search(10)
1234
>>> search(6000)
3124

答案 2 :(得分:2)

一个完全不同(但很快)的选项是使用bisect模块并简单地添加起点和值(假设你的范围是连续的)。

import bisect

ranges = (
    (0, 1234),
    (5001, 1231),
    (10001, 3242),
    (50001, 3543),
    (100001, 2303),
)

def find_range(value):
    min_ = ranges[0][0]
    if min_ > value:
        raise ValueError('Values smaller than %d are not supported' % min_)

    # Search for the insert point using bisect but add 1 so we handle
    # corner cases correctly
    key = value + 1, 0

    # Use bisect to find the index
    index = bisect.bisect(ranges, key)

    # Return the 2nd item from the tuple since that contains the ID
    start_index, id_ = ranges[index - 1]
    return id_

print 'Testing standard ranges'
for i in range(15):
    i = 2 ** i
    print 'Looking for %d, got: %d' % (i, find_range(i))

print
print 'Testing corner cases:'
for start, id_ in ranges:
    for i in range(start - 1, start + 2):
        try:
            value = find_range(i)
        except ValueError, value:
            pass

        print 'Looking for %d, got: %s' % (i, value)

结果:

Testing standard ranges
Looking for 1, got: 1234
Looking for 2, got: 1234
Looking for 4, got: 1234
Looking for 8, got: 1234
Looking for 16, got: 1234
Looking for 32, got: 1234
Looking for 64, got: 1234
Looking for 128, got: 1234
Looking for 256, got: 1234
Looking for 512, got: 1234
Looking for 1024, got: 1234
Looking for 2048, got: 1234
Looking for 4096, got: 1234
Looking for 8192, got: 1231
Looking for 16384, got: 3242

Testing corner cases:
Looking for -1, got: Values smaller than 0 are not supported
Looking for 0, got: 1234
Looking for 1, got: 1234
Looking for 5000, got: 1234
Looking for 5001, got: 1231
Looking for 5002, got: 1231
Looking for 10000, got: 1231
Looking for 10001, got: 3242
Looking for 10002, got: 3242
Looking for 50000, got: 3242
Looking for 50001, got: 3543
Looking for 50002, got: 3543
Looking for 100000, got: 3543
Looking for 100001, got: 2303
Looking for 100002, got: 2303

答案 3 :(得分:1)

我可以假设存储的范围不会在它们之间留下空隙吗?

我会:

  • 将映射存储为dict(range_start - &gt;值),就像您一样
  • 获取密钥K的值:
    • 对dict键进行二进制搜索,找到小于或等于K(O(logN))的最大键
    • 返回该键的值(O(1))。

答案 4 :(得分:1)

在Python 3.4中,您可以使用范围作为键。但是因为你使用的是2.7,所以这不是一个选择。但对于其他读者来说,也许值得考虑。

d = {
  range(0, 5000): 1234,
  range(5001, 10000): 1231,
  range(10001, 20000): 3242
}

x = 6000
r = [i[1] for i in d.items() if x in i[0]][0]
# r == 1231

您可以对此进行改进,以了解x不在任何范围内的情况。

答案 5 :(得分:0)

这是Wolph的竞争对手,使用他的代码,但改变了:

import bisect

range_stops = [0, 4, 8]
range_ids   = [0, 1, 2]

def find_range(value):
    # Need to limit to `0` because there is no -1th index
    index = max(0, bisect.bisect_right(range_stops, value)-1)

    return range_ids[index]

for i in range(-2, 10):
    print("{:2} → {}".format(i, find_range(i)))

#>>> -2 → 0
#>>> -1 → 0
#>>>  0 → 0
#>>>  1 → 0
#>>>  2 → 0
#>>>  3 → 0
#>>>  4 → 1
#>>>  5 → 1
#>>>  6 → 1
#>>>  7 → 1
#>>>  8 → 2
#>>>  9 → 2

这稍微简单一些,但需要两个数据结构而不是一个。从好的方面来说,工作要容易得多。它还将支持浮点范围和索引。