【问题标题】:Building a "complete" number range w/out overlaps建立一个没有重叠的“完整”数字范围
【发布时间】:2009-02-19 18:33:44
【问题描述】:

我需要在给定一系列数字的情况下构建一个完整的“数字范围”集。我从一个列表开始,例如:

ID   START  
*    0  
a    4  
b    70  
c    700  
d    701  
e    85  
  • 其中“def”是默认范围,应该“填补”空白
  • “重叠”是起始数据中的值(70、700、701)

并且需要以下结果:

ID  START  END  
*     0 - 39  
a     4 - 49  
*     5 - 69  
c   700 - 7009  
d   701 - 7019  
b   702 - 709  
*    71 - 849  
e    85 - 859  
*    86 - 9  

我想弄清楚是否有某种算法或设计模式可以解决这个问题。我有一些想法,但我想我会先由“专家”来运行它。我正在使用 Python。

任何想法/方向将不胜感激。我有一些初步的想法:

  • 构建一个“范围”列表,将开始和结束值填充到全长。所以默认是 0000 到 9999
  • 构建动态构建的“拆分”列表
  • 循环遍历“范围”列表,将每个值与拆分列表中的值进行比较。
  • 如果发现重叠,请删除拆分列表中的值并添加新范围。

【问题讨论】:

    标签: python range numbers overlap


    【解决方案1】:
    import operator
    
    ranges = {
        '4'  : 'a',
        '70' : 'b',
        '700': 'c',
        '701': 'd',
        '85' : 'e',
        '87' : 'a',
    }
    
    def id_for_value(value):
        possible = '*'
        for idvalue, id in sorted(ranges.iteritems()):
            if value.startswith(idvalue):
                possible = id
            elif idvalue > value:
                break
        return possible
    

    知道某个值的id就足够了。测试:

    assert id_for_value('10') == '*'
    assert id_for_value('499') == 'a'
    assert id_for_value('703') == 'b'
    assert id_for_value('7007') == 'c'
    assert id_for_value('7017') == 'd'
    assert id_for_value('76') == id_for_value('83') == '*'
    assert id_for_value('857') == 'e'
    assert id_for_value('8716') == 'a'
    

    如果你真的想要范围,你可以使用 itertools.groupby 来计算:

    def firstlast(iterator):
        """ Returns the first and last value of an iterator"""
        first = last = iterator.next()
        for value in iterator:
            last = value
        return first, last
    
    maxlen = max(len(x) for x in ranges) + 1
    test_range = ('%0*d' % (maxlen, i) for i in xrange(10 ** maxlen))
    result = dict((firstlast(gr), id) 
                  for id, gr in itertools.groupby(test_range, key=id_for_value))
    

    给予:

    {('0000', '3999'): '*',
     ('4000', '4999'): 'a',
     ('5000', '6999'): '*',
     ('7000', '7009'): 'c',
     ('7010', '7019'): 'd',
     ('7020', '7099'): 'b',
     ('7100', '8499'): '*',
     ('8500', '8599'): 'e',
     ('8600', '8699'): '*',
     ('8700', '8799'): 'a',
     ('8800', '9999'): '*'}
    

    【讨论】:

    • 哇!让我试试这个......我遗漏的一个复杂性是您可以为同一个“ID”拥有多个范围,这会影响初始字典。谢谢!!
    • @John:我已经更新了我的答案以反映同一个 ID 的多个范围。我现在只是使用 id 作为值。
    • @Nosklo -- 出于某种原因,当我的 START 范围很大 (7190000) 时,进程内存不足。不管我有 3 个范围还是 20 个范围,这样大小的范围都会导致内存问题。想法?再次感谢!
    • @Nosklo -- 我知道为什么它会遇到内存问题(我相信你也这样做)。有了这么大的范围,maxlen 变成了 1000 万,我们现在正在处理一个更大的数据集。我们能以某种方式只构建 test_range 的子集吗?那么 - 那个尺寸只有 7190000 左右?
    • @John:你的内存用完了,因为 list() 函数实际上是在内存中创建一个大列表。我已经编辑了答案并添加了一个不会创建列表的 firstlast 函数。这将防止内存错误,但速度很慢。如果您需要大范围,也许您可​​以采取另一种方法。
    猜你喜欢
    • 1970-01-01
    • 2013-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-20
    • 1970-01-01
    相关资源
    最近更新 更多