【问题标题】:Finding data gaps with bit masking使用位掩码查找数据间隙
【发布时间】:2010-12-07 09:54:13
【问题描述】:

我遇到了在数字序列中查找给定长度的不连续性(间隙)的问题。因此,例如,给定[1,2,3,7,8,9,10]length=3 的差距,我会找到[4,5,6]。如果差距是length=4,我什么也找不到。当然,真正的序列要长得多。我在很多帖子中都看到过这个问题,它有各种应用和可能的实现。

我认为可能可行并且应该相对较快的一种方法是将完整集表示为一个位数组,其中包含 1 表示可用数字和 0 表示缺失 - 所以上面看起来像 [1,1,1,0,0,0,1,1,1,1]。然后可能运行一个窗口函数,它将用完整的集合对给定长度的数组进行 XOR 掩码,直到所有位置的结果为 1。这将需要在大约 ~O(n) 内对整个序列进行单次传递,加上成本在每次运行中进行掩蔽。

这是我设法想出的:

def find_gap(array, start=0, length=10):
    """
    array:  assumed to be of length MAX_NUMBER and contain 0 or 1 
            if the value is actually present
    start:  indicates what value to start looking from
    length: what the length the gap should be
    """

    # create the bitmask to check against
    mask = ''.join( [1] * length )

    # convert the input 0/1 mapping to bit string
    # e.g - [1,0,1,0] -> '1010'
    bits =''.join( [ str(val) for val in array ] )

    for i in xrange(start, len(bits) - length):

        # find where the next gap begins
        if bits[i] != '0': continue

        # gap was found, extract segment of size 'length', compare w/ mask
        if (i + length < len(bits)):
            segment = bits[i:i+length]

            # use XOR between binary masks
            result  = bin( int(mask, 2) ^ int(segment, 2) )

            # if mask == result in base 2, gap found
            if result == ("0b%s" % mask): return i

    # if we got here, no gap exists
    return -1

这对于大约 100k(

【问题讨论】:

  • start 是数组 idx 而不是值?
  • 我一定没有正确理解这个问题。你不能只寻找a[i + 1] - a[i] == gap + 1的相邻元素吗?
  • @Marcelo 我认为你真的可以,并且 OP 严重地将事情复杂化了,可能是基于一些关于优化的知之甚少的想法。我根据这个假设写了我的答案。
  • 了解您是否要在同一序列中查找多个不同长度的间隙非常重要。如果您想迭代地查找长度为 1 到 100 的间隙,那么首先转换序列可能是值得的。
  • 您是否只是想要一个列表来补充提供的列表,其中的间隙与请求的确切大小相匹配? [1,2,5,8] 长度=2 -> [3,4,6,7]?

标签: python arrays performance bitmask


【解决方案1】:

找出相邻数字之间的差异,然后寻找足够大的差异。我们通过构建两个列表来找到差异——除了第一个之外的所有数字,以及除了最后一个之外的所有数字——然后成对地减去它们。我们可以使用zip 将值配对。

def find_gaps(numbers, gap_size):
    adjacent_differences = [(y - x) for (x, y) in zip(numbers[:-1], numbers[1:])]
    # If adjacent_differences[i] > gap_size, there is a gap of that size between
    # numbers[i] and numbers[i+1]. We return all such indexes in a list - so if
    # the result is [] (empty list), there are no gaps.
    return [i for (i, x) in enumerate(adjacent_differences) if x > gap_size]

(另外,学习一些 Python 习语。我们更喜欢直接迭代,而且我们有一个真正的布尔类型。)

【讨论】:

  • 他不想要差异 >= 差距大小,只 == 差距大小。
  • 所以,... if x == gap_size + 1(根据描述,在 3 和 7 之间有一个大小为 3 的间隙,因此间隙大小比差值小一)。 :)
  • 好的,+1 (dto. 为你的答案;))
  • 非常酷,但我需要对其进行一些调整以使其适合我。不确定直接迭代是什么意思。我知道布尔类型,只使用了 0/1,因为它对我的版本效果更好:) - 谢谢!
【解决方案2】:

您可以使用 XOR 和 shift,它确实在大约 O(n) 时间内运行。

但是,在实践中,构建索引(所有间隙的哈希列表大于某个最小长度)可能是更好的方法。

假设您从这些整数的序列(而不是位掩码)开始,那么您只需遍历序列即可构建索引;每当您发现大于阈值的间隙时,您都会将该间隙大小添加到字典中(如有必要,将其实例化为空列表,然后在序列中附加偏移量。

最后,您有一个序列中每个间隙(大于您所需的阈值)的列表。

这种方法的一个好处是您应该能够在修改基本列表时维护该索引。因此,构建索引所花费的 O(n*log(n)) 初始时间由后续查询和更新索引的 O(log(n)) 成本分摊。

这是构建gap_index()的一个非常粗略的函数:

def gap_idx(s, thresh=2):
    ret = dict()
    lw = s[0]  # initial low val.
    for z,i in enumerate(s[1:]):
        if i - lw < thresh:
            lw = i
            continue
        key = i - lw
        if key not in ret:
            ret[key] = list()
        ret[key].append(z)
        lw = i
    return ret

维护数据集和索引的类最好围绕内置的“bisect”模块及其insort()函数构建。

【讨论】:

    【解决方案3】:

    与 aix 所做的差不多……但只获得了所需长度的间隙:

    def findGaps(mylist, gap_length, start_idx=0):
        gap_starts = []
        for idx in range(start_idx, len(mylist) - 1):
            if mylist[idx+1] - mylist[idx] == gap_length + 1:
                gap_starts.append(mylist[idx] + 1)
    
        return gap_starts
    

    编辑:根据 OP 的意愿进行调整。

    【讨论】:

      【解决方案4】:

      这些提供了您输入列表的单步走。

      给定长度的间隙值列表:

      from itertools import tee, izip
      def gapsofsize(iterable, length):
          a, b = tee(iterable)
          next(b, None)
          return ( p for x, y in izip(a, b) if y-x == length+1 for p in xrange(x+1,y) )
      
      print list(gapsofsize([1,2,5,8,9], 2))
      
      [3, 4, 6, 7]
      

      所有间隙值:

      def gaps(iterable):
          a, b = tee(iterable)
          next(b, None)
          return ( p for x, y in izip(a, b) if y-x > 1 for p in xrange(x+1,y) )
      
      print list(gaps([1,2,4,5,8,9,14]))
      
      [3, 6, 7, 10, 11, 12, 13]
      

      作为向量的间隙列表:

      def gapsizes(iterable):
          a, b = tee(iterable)
          next(b, None)
          return ( (x+1, y-x-1) for x, y in izip(a, b) if y-x > 1 )
      
      print list(gapsizes([1,2,4,5,8,9,14]))
      
      [(3, 1), (6, 2), (10, 4)]
      

      请注意,这些是生成器,消耗的内存非常少。 我很想知道这些在您的测试数据集上的表现。

      【讨论】:

        【解决方案5】:

        如果您追求的是效率,我会按照以下几行做一些事情(其中x 是序列号列表):

        for i in range(1, len(x)):
          if x[i] - x[i - 1] == length + 1:
            print list(range(x[i - 1] + 1, x[i]))
        

        【讨论】:

          猜你喜欢
          • 2021-11-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多