【问题标题】:Element at index in Itertools.productItertools.product 中索引处的元素
【发布时间】:2019-05-18 23:16:54
【问题描述】:

我们可以从 Python 3 中的 itertools.product 结果中获取特定索引中的元素吗?如下图:

xlist = itertools.product('abc', repeat=3)
element = xlist[10]

更新
整个问题都颠倒了!我发现生成所有集合并查找索引是一个很大的错误!我查看了我的问题可能的duplicate,但我没有得到answer

【问题讨论】:

标签: python itertools


【解决方案1】:

其他解决方案会产生正确的结果,但如果您只想要笛卡尔积的一个元素,那么迭代 itertools.product 返回的生成器并不是最有效的解决方案。您可以直接构建您需要的项目,而无需使用如下函数遍历所有元素:

from collections.abc import Sequence

def product_item(idx, *seqs, repeat=None):
    # Ensure inputs are actual sequences (list, tuple, str...)
    seqs = [seq if isinstance(seq, Sequence) else list(seq) for seq in seqs]
    # Repeat if needed
    if repeat is not None:
        seqs = seqs * repeat
    # Compute how many items does it take to advance on each sequence
    step = 1
    for seq in seqs:
        step *= len(seq)
    # Build product item
    item = [None] * len(seqs)
    for i, seq in enumerate(seqs):
        step //= len(seq)
        seq_idx = idx // step
        idx %= step
        item[i] = seq[seq_idx]
    return tuple(item)

print(product_item(10, 'abc', repeat=3))
# ('b', 'a', 'b')

此解决方案的复杂度为 O(1)。快速比较:

import itertools

# Solution with islice
product_item_islice = lambda idx, *seqs, repeat=None: next(
    itertools.islice(itertools.product(*seqs, repeat=repeat), idx, None))

idx = 100_000_000
seqs = ['abcdefgh']
repeat = 10
print(product_item(idx, *seqs, repeat=repeat))
# ('a', 'f', 'h', 'f', 'd', 'g', 'a', 'e', 'a', 'a')
print(product_item_islice(idx, *seqs, repeat=repeat))
# ('a', 'f', 'h', 'f', 'd', 'g', 'a', 'e', 'a', 'a')

%timeit product_item(idx, *seqs, repeat=repeat)
# 3.7 µs ± 46.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit product_item_islice(idx, *seqs, repeat=repeat)
# 448 ms ± 7.55 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

【讨论】:

    【解决方案2】:

    在到达索引之前,您不必迭代生成器,但您可以立即生成该产品,在 O(1) 中,如 here 所述。将其调整为 (lst, repeat) 表单:

    def nth_product(lst, repeat, n):
        k = len(lst)
        return [lst[n // (k**r) % k] for r in range(repeat-1, -1, -1)]
    

    例子:

    >>> lst = list(range(10))
    >>> ref = list(itertools.product(lst, repeat=3))
    >>> all(nth_product(lst, 3, i) == list(r) for i, r in enumerate(ref))
    True
    

    【讨论】:

      【解决方案3】:

      itertools.product() 的结果不是一个列表,而是一个可迭代对象 - 这就是为什么这不起作用,您无法通过索引访问可迭代对象。

      您可以使用list() 创建一个列表 - 但这意味着计算 所有 值,这可能非常低效。在示例情况下,虽然我们必须计算所有值,直到我们想要的值,我们不需要将它们全部存储在内存中,也不需要在我们得到我们想要的值之后计算其余的值。

      如果您只想要一个元素,更好的解决方案是只使用您需要的部分 - 这可以通过 itertools.islice() 轻松完成。

      element = next(itertools.islice(xlist, 10, None))

      由于切片是另一个可迭代的,我们使用next() 来获取第一项。

      islice 的功能与列表切片非常相似(顾名思义)。请注意,一旦您消耗了一些可迭代对象,就可以从您离开的地方继续使用它。您应该在一次迭代中获得所需的内容,或者创建一个列表(或其他适当的数据结构)。

      islice 与其他丢弃您不感兴趣的初始值的方式相比的最大优势在于,它在 Python 中非常有效地实现,因此可能是最快的选择,并且在以下情况下非常灵活你以后需要的不仅仅是一个元素。

      【讨论】:

        【解决方案4】:

        假设您参考 Python 3,您可以使用 enumerate 过滤以索引生成器,然后使用 next

        import itertools
        
        it = enumerate(itertools.product('abc', repeat=3))
        result = next(e for i, e in it if i == 10)
        print(result)
        

        输出

        ('b', 'a', 'b')
        

        【讨论】:

          猜你喜欢
          • 2020-03-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-02-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多