如何计算两个列表的所有交错?

Posted

技术标签:

【中文标题】如何计算两个列表的所有交错?【英文标题】:How to calculate all interleavings of two lists? 【发布时间】:2013-03-09 01:45:45 【问题描述】:

我想创建一个接收两个列表的函数,这些列表不保证长度相等,并返回两个列表之间的所有交错。

输入:两个大小不必相等的列表。

输出:保留原始列表顺序的两个列表之间的所有可能交错。

示例: AllInter([1,2],[3,4]) -> [[1,2,3,4], [1,3,2,4], [ 1,3,4,2], [3,1,2,4], [3,1,4,2], [3,4,1,2]]

我不想要解决方案。我想要一个提示。

【问题讨论】:

提示:导入 itertools 这是一个惊人的提示 我在 python 中的大部分列表问题都是通过记住它的存在来解决的 当然。大多数 itertools 没有解决的问题都可以通过查看其文档中的配方来解决。 【参考方案1】:

Itertools 不足以处理这个问题,需要对钉子和孔问题有一些了解

考虑您的示例列表

A = [1, 2] B = [3, 4]

有四个孔 (len(A) + len(B)),您可以在其中放置元素(钉子)

|   ||   ||   ||   |
|___||___||___||___|

在 Python 中,您可以将空槽表示为 None 的列表

slots = [None]*(len(A) + len(B))

您可以将第二个列表中的元素(钉子)放入钉子的方式数量很简单,您可以从孔中选择插槽的方式数量是

len(A) + len(B)Clen(B)

= 4C2

= itertools.combinations(range(0, len(len(A) + len(B)))

可以描述为

|   ||   ||   ||   |  Slots
|___||___||___||___|  Selected

  3    4              (0,1)
  3         4         (0,2)
  3              4    (0,3) 
       3    4         (1,2) 
       3         4    (1,3)
            3    4    (2,3)   

现在为每个插槽位置填充第二个列表中的元素(钉)

for splice in combinations(range(0,len(slots)),len(B)):
    it_B = iter(B)
    for s in splice:
        slots[s] = next(it_B)

完成后,您只需使用第一个列表中的元素(钉子)填充剩余的空槽

    it_A = iter(A)
    slots = [e if e else next(it_A) for e in slots]

在使用另一个插槽位置开始下一次迭代之前,请冲洗孔

    slots = [None]*(len(slots))

上述方法的 Python 实现

def slot_combinations(A,B):
    slots = [None]*(len(A) + len(B))
    for splice in combinations(range(0,len(slots)),len(B)):
        it_B = iter(B)
        for s in splice:
            slots[s] = next(it_B)
        it_A = iter(A)
        slots = [e if e else next(it_A) for e in slots]
        yield slots
        slots = [None]*(len(slots))

以及上面实现的O/P

list(slot_combinations(A,B))
[[3, 4, 1, 2], [3, 1, 4, 2], [3, 1, 2, 4], [1, 3, 4, 2], [1, 3, 2, 4], [1, 2, 3, 4]]

【讨论】:

这是一个针对钉子和孔洞问题的惰性解决方案(它在子列表中使用生成器):interleave_where() 啊,我们刚刚得到了完整的实现,而不是提示。不错。 很好的解释。您可能只是说我的问题是挂钩和孔问题的一个实例,但没有提供实现。在我接受你的答案之前,你能不能先校对一下(我觉得解释有错别字,不应该是= itertools.combinations(range(0, len(A) + len(B)), 2)吗?),并为“挂钩和洞”问题提供前提?我试过谷歌搜索,但只发现“方形钉和圆孔”问题或类似问题。如果你提供一个好的前提,我什至建议你编辑掉实现部分,但这是你的选择。【参考方案2】:

提示:假设每个列表具有相同的元素(但列表之间不同),即一个列表完全是红色的(比如其中的 r 个),另一个是蓝色的(比如其中的 b 个)。

输出的每个元素都包含 r+b 或它们,其中 r 是红色的。

似乎别人已经为你宠坏了,即使你没有要求解决方案(但他们有一个很好的解释)

所以这里是我快速编写的代码。

import itertools

def interleave(lst1, lst2):
    r,b = len(lst1), len(lst2)
    for s in itertools.combinations(xrange(0,r+b), r):
        lst = [0]*(r+b)
        tuple_idx,idx1,idx2 = 0,0,0
        for i in xrange(0, r+b):
            if tuple_idx < r and i == s[tuple_idx]:
                lst[i] = lst1[idx1]
                idx1 += 1
                tuple_idx += 1
            else: 
                lst[i] = lst2[idx2]
                idx2 += 1
        yield lst


def main():
    for s in interleave([1,2,3], ['a','b']):
        print s

if __name__ == "__main__":
    main()

基本思想是将输出映射到 (r+b) 选择 r 个组合。

【讨论】:

【参考方案3】:

正如@airza 所建议的,itertools 模块是你的朋友。

如果你想避免使用封装好的魔法,我的建议是使用递归。

开始在你的脑海中播放生成列表的过程,当你发现你又在做同样的事情时,尝试找到模式。例如:

    从第一个列表中取出第一个元素 从另一个列表中选择第二个或第一个 要么选择第 3 名,要么选择第 2 名(如果没有),或者从另一个列表中选择另一个 ...

好的,看起来我们没有使用一些更大的逻辑。我只是增加数字。当然,我可以找到一个在更改“第一个元素而不是命名更高元素”时有效的基本案例?

玩它。 :)

【讨论】:

【参考方案4】:

您可以尝试一些更接近金属且更优雅(在我看来)的方法,以迭代不同的可能切片。基本上逐步遍历并遍历标准切片操作的所有三个参数,删除添加到最终列表中的任何内容。有兴趣可以发代码sn-p。

【讨论】:

以上是关于如何计算两个列表的所有交错?的主要内容,如果未能解决你的问题,请参考以下文章

两个字符串的交错

如何交错来自两个文本文件的行

如何防止两个操作相互交错,同时仍然允许并发执行?

如何将两个 Rust 向量按三个块交错成一个新向量?

Kotlin:合并多个列表然后排序交错合并列表

交错2个长度不等的列表[重复]