列表环绕以找到索引之间的距离

3

我有一个随机生成的列表,可能看起来像这样:

[1, 0, 0, 1, 1, 0, 1, 0, 0, 0]

我需要找出所有1之间的距离,包括循环的部分。

举个例子,在上面的列表中,第一个1到下一个1的距离为3。第二个1到下一个1的距离为1,依此类推。

如何使用循环到第一个1找出列表中最后一个1的距离?

def calc_dist(loc_c):
   first = []
   #lst2 = []
   count = 0
   for i in range(len(loc_c)):
       if loc_c[i] == 0:
           count += 1
           #lst2.append(0)
       elif loc_c[i] == 1:
           first.append(i)
           count += 1
           loc_c[i] = count
           #lst2.append(loc_c[i])
           #if loc_c[i] + count > len(loc_c):
               # x = loc_c[first[0] + 11 % len(loc_c)]
               # loc_c[i] = x
           count = 0

   return loc_c

我期望的结果应该是[3, 1, 2, 4]。

2
那么“距离”是当前“1”和右侧下一个“1”的索引之间的差异吗?只需存储第一个“1”的索引,这就是距离末尾的距离。如果最后一个1不在末尾,则将其添加到距离中。 - Martijn Pieters
如果在这种情况下它是4,那么找到第一个1的索引和最后一个1的索引,然后你的结果将是列表长度-最后一个1的索引+第一个1的索引。 - ashish pal
3个回答

5

存储第一次引用的 1 的索引,当到达最后一个 1 时,只需添加第一个索引加上最后一个 1 后面的 0 元素数量以获取该距离(即 len(inputlist) - lastindex + firstindex)。

其他距离是前一个 1 值与当前索引之间的差值。

from typing import Any, Generator, Iterable

def distances(it: Iterable[Any]) -> Generator[int, None, None]:
    """Produce distances between true values in an iterable.

    If the iterable is not endless, the final distance is that of the last
    true value to the first as if the sequence of values looped round.

    """
    first = prev = None
    length = 0
    for i, v in enumerate(it):
        length += 1
        if v:
            if first is None:
                first = i
            else:
                yield i - prev
            prev = i
    if first is not None:
        yield length - prev + first

上述生成器在循环遍历序列seq时计算距离,并逐个生成它们:
>>> for distance in distances([1, 0, 0, 1, 1, 0, 1, 0, 0, 0]):
...     print(distance)
...
3
1
2
4

如果您必须要获得列表输出,请在生成器上调用list()

>>> list(distances([1, 0, 0, 1, 1, 0, 1, 0, 0, 0]))
[3, 1, 2, 4]

如果没有 1 值,则距离为零:
>>> list(distances([0, 0, 0]))
[]

当输入1 1的数值时,可以得到1的距离:

>>> list(distances([1, 0, 0]))
[3]

我将解决方案通用化,使其能够处理任何可迭代对象,即使是无限的;这意味着您也可以使用另一个生成器来提供输入。如果给定一个产生至少一些非零值的无限可迭代对象,它将不断地产生距离。

2

整洁清爽:

def calc_dist(l):
    idx = [i for i, v in enumerate(l) if v]
    if not idx: return []
    idx.append(len(l)+idx[0])
    return [idx[i]-idx[i-1] for i in range(1,len(idx))]

print(calc_dist([1, 0, 0, 1, 1, 0, 1, 0, 0, 0]))
# [3, 1, 2, 4]
print(calc_dist([0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0]))
# [3, 1, 2, 7]
print(calc_dist([0, 0, 0, 0])
# []

我会选择这个作为最佳答案,因为它既简洁又易于理解,适合其他人阅读。 - Keegan Husom
它还不完美,因为我不知道如果只有一个 1 时你想要做什么。例如, [0, 1, 0] 将返回 3,这可能不是你想要的? - Bram Vanroy

1
你可以使用numpy:
import numpy as np

L = np.array([1, 0, 0, 1, 1, 0, 1, 0, 0, 0])
id = np.where(test == 1)[0]

# id = array([0, 3, 4, 6], dtype=int64)

res = [id[i]-id[i-1] for i in range(1, len(id))]
# [3, 1, 2]

# Last distance missing:
res.append(len(L)- id[-1])

res = [3, 1, 2, 4]

请注意,您所要求的信息已经包含在上面了,但可能输出格式不正确。您没有给出具体要求...
编辑:如何将列表转换为数组,因为您生成了随机列表
L = [1, 0, 0, 1, 1, 0, 1, 0, 0, 0]
np.asarray(L)

Edit2: 如何检查列表中是否没有1:

import numpy as np

L = np.array([1, 0, 0, 1, 1, 0, 1, 0, 0, 0])
id = np.where(test == 1)[0]

if len(id) == 0:
    res = []
else:
    res = [id[i]-id[i-1] for i in range(1, len(id))]
    res.append(len(L)- id[-1])

OR:

try:
    res = [id[i]-id[i-1] for i in range(1, len(id))]
    res.append(len(L)- id[-1])
except:
    res = []

@PM2Ring 我已经完成了这部分:“例如上面的列表,第一个1到下一个1的距离为3。第二个1到下一个1的距离为1,依此类推。” 另一个我不太明白他想要什么。 - Mathieu
不,你需要4个距离,你缺少最后一个距离为4。 - Martijn Pieters
@MartijnPieters 好的,我第一次读没看懂。在编辑中已修复。 - Mathieu
@MartijnPieters 我很好奇能否测试一下你的生成器解决方案和我的numpy解决方案,看看哪个更优秀 :) - Mathieu
1
我的生成器循环是解释字节码的,你的则基于手动优化的numpy本地编译代码。我想我知道它会走向哪里;-) - Martijn Pieters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接