计算自上一个头以来的尾部数量

时间:2017-07-22 17:04:48

标签: python algorithm sequence

考虑一系列硬币投掷:1,0,0,1,0,1,其中tail = 0且head = 1.

所需的输出是序列:0,1,2,0,1,0

输出序列的每个元素都计算自上一个头以来的尾部数量。

我尝试过一种天真的方法:

def timer(seq):
    if seq[0] == 1: time = [0]
    if seq[0] == 0: time = [1]
    for x in seq[1:]:
        if x == 0: time.append(time[-1] + 1)
        if x == 1: time.append(0)
    return time

问题:有更好的方法吗?

6 个答案:

答案 0 :(得分:5)

使用NumPy:

import numpy as np 
seq = np.array([1,0,0,1,0,1,0,0,0,0,1,0])
arr = np.arange(len(seq))
result = arr - np.maximum.accumulate(arr * seq)
print(result)

产量

[0 1 2 0 1 0 1 2 3 4 0 1]

为什么arr - np.maximum.accumulate(arr * seq)所需的输出似乎与整数的简单进展有关:

arr = np.arange(len(seq))

所以,自然的问题是,如果seq = np.array([1, 0, 0, 1, 0, 1])且预期结果是expected = np.array([0, 1, 2, 0, 1, 0]),那么x的值是什么

arr + x = expected

由于

In [220]: expected - arr
Out[220]: array([ 0,  0,  0, -3, -3, -5])

看起来x应该是arr * seq的累积最大值:

In [234]: arr * seq
Out[234]: array([0, 0, 0, 3, 0, 5])

In [235]: np.maximum.accumulate(arr * seq)
Out[235]: array([0, 0, 0, 3, 3, 5])

答案 1 :(得分:3)

第1步: 反转l

In [311]: l = [1, 0, 0, 1, 0, 1]

In [312]: out = [int(not i) for i in l]; out
Out[312]: [0, 1, 1, 0, 1, 0]

第2步: 列出补偿;如果当前值为1,则将先前值添加到当前值。

In [319]: [out[0]] + [x + y if y else y for x, y in zip(out[:-1], out[1:])]
Out[319]: [0, 1, 2, 0, 1, 0]

通过压缩相邻的元素,摆脱了多风的情况。

答案 2 :(得分:3)

使用itertools.accumulate

>>> a = [1, 0, 0, 1, 0, 1]
>>> b = [1 - x for x in a]
>>> list(accumulate(b, lambda total,e: total+1 if e==1 else 0))
[0, 1, 2, 0, 1, 0]

accumulate仅在Python 3中定义。但是,如果您想在Python 2中使用它,那么上述文档中的等效Python代码。

需要反转a,因为accumulate返回的第一个元素是第一个列表元素,与累加器函数无关:

>>> list(accumulate(a, lambda total,e: 0))
[1, 0, 0, 0, 0, 0]

答案 3 :(得分:1)

所需的输出是一个与输入长度相同的数组,并且所有值都不等于输入。因此,算法必须至少为O(n)才能形成新的输出数组。此外,对于此特定问题,您还需要扫描输入数组的所有值。所有这些操作都是O(n),它不会更有效。常量可能不同,但您的方法已经在O(n)中,并且不会更低。

答案 4 :(得分:0)

使用reduce

time = reduce(lambda l, r: l + [(l[-1]+1)*(not r)], seq, [0])[1:]

答案 5 :(得分:0)

我尝试在以下代码中清楚,并且与使用显式累加器的原始代码不同。

>>> s = [1,0,0,1,0,1,0,0,0,0,1,0]
>>> def zero_run_length_or_zero(seq):
    "Return the run length of zeroes so far in the sequnece or zero"
    accumulator, answer = 0, []
    for item in seq:
        accumulator = 0 if item == 1 else accumulator + 1
        answer.append(accumulator)
    return answer

>>> zero_run_length_or_zero(s)
[0, 1, 2, 0, 1, 0, 1, 2, 3, 4, 0, 1]
>>>