通过位屏蔽查找数据间隙

时间:2010-12-07 09:54:13

标签: python arrays performance bitmask

我遇到了在一系列数字中发现给定长度的不连续性(间隙)的问题。因此,例如,给定[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(<1秒)。我很欣赏如何为更大的设备提供更快/更高效的技巧。谢谢!

5 个答案:

答案 0 :(得分:2)

找出相邻数字之间的差异,然后寻找足够大的差异。我们通过构建两个列表找到差异 - 所有数字但是第一个,所有数字但是最后一个 - 并且成对地减去它们。我们可以使用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习语。我们更喜欢直接迭代,我们有一个真正的布尔类型。)

答案 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()函数构建。

答案 2 :(得分:1)

如果你的效率很高,我会按照以下几行做一些事情(其中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]))

答案 3 :(得分:1)

几乎是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 :(得分:1)

这些提供了输入列表的单个步骤。

给定长度的间隙值列表:

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)]

请注意,这些是生成器,占用的内存非常少。 我很想知道这些对您的测试数据集的影响。