无论顺序如何,都生成重复列表

时间:2013-12-02 15:24:01

标签: python algorithm permutation combinatorics

我想生成将列表中的索引与“slots”相关联的组合。例如,(0, 0, 1)表示0和1属于同一个插槽,而2属于另一个插槽。 (0, 1, 1, 1)表示1,2,3属于同一个插槽,而0本身属于同一个插槽。在这个例子中,0和1只是识别这些插槽的方法,但不包含我使用的信息。

因此,(0, 0, 0)与我的目的完全相同(1, 1, 1),而(0, 0, 1)相当于(1, 1, 0)

经典的笛卡尔积产生了许多我想摆脱的重复。

这是我通过itertools.product获得的:

>>> LEN, SIZE = (3,1)
>>> list(itertools.product(range(SIZE+1), repeat=LEN))
>>>
[(0, 0, 0),
(0, 0, 1),
(0, 1, 0),
(0, 1, 1),
(1, 0, 0),
(1, 0, 1),
(1, 1, 0),
(1, 1, 1)]

这就是我想要的:

>>> [(0, 0, 0),
(0, 0, 1),
(0, 1, 0),
(0, 1, 1)]

使用小型列表很容易,但我不太清楚如何使用更大的集合来完成此操作。你有什么建议吗?

如果不清楚,请告诉我,以便我澄清我的问题。谢谢!

编辑:根据Sneftel的回答,此功能似乎有效,但我不知道它是否真的产生了所有结果:

def test():
    for p in product(range(2), repeat=3):
        j=-1
        good = True
        for k in p:
            if k> j and (k-j) > 1:
                good = False
            elif k >j:
                j = k
        if good:
            yield p

4 个答案:

答案 0 :(得分:2)

我首先要做出以下观察:

  1. 每个组合的第一个元素必须为0.
  2. 第二个元素必须为0或1.
  3. 第三个元素必须是0,1或2,但是如果第二个元素是1 ,它只能是2。
  4. 这些观察结果表明了以下算法:

    def assignments(n, m, used=0):
        """Generate assignments of `n` items to `m` indistinguishable
        buckets, where `used` buckets have been used so far.
    
            >>> list(assignments(3, 1))
            [(0, 0, 0)]
            >>> list(assignments(3, 2))
            [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1)]
            >>> list(assignments(3, 3))
            [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (0, 1, 2)]
    
        """
        if n == 0:
            yield ()
            return
        aa = list(assignments(n - 1, m, used))
        for first in range(used):
            for a in aa:
                yield (first,) + a
        if used < m:
            for a in assignments(n - 1, m, used + 1):
                yield (used,) + a
    

    这会在几秒钟内处理您的用例(12个项目,5个存储桶):

    >>> from timeit import timeit
    >>> timeit(lambda:list(assignments(12, 5)), number=1)
    4.513746023178101
    >>> sum(1 for _ in assignments(12, 5))
    2079475
    

    这比你在答案结束时给出的函数(调用product然后删除无效赋值的函数)快得多,如果它被修改为处理(12,5)使用情况下:

    >>> timeit(lambda:list(test(12, 5)), number=1)
    540.693009853363
    

答案 1 :(得分:1)

在检查重复项之前,您应该协调符号(假设您不想设置一些花哨的AI):迭代列表并为从0开始向上计数的不同元素分配集合从属关系数。也就是说,您要为每个正在处理的行创建一个临时字典。

示例性输出将是

(0,0,0) -> (0,0,0)
(0,1,0) -> (0,1,0)

(1,0,1) -> (0,1,0)

然后可以轻松执行删除重复项,因为问题已解决为Python : How to remove duplicate lists in a list of list?

中已解决问题的问题

答案 2 :(得分:1)

如果您只考虑笛卡尔积的元素,其中所有索引的第一次出现被排序并从零开始连续,那么这应该足够了。 itertools.combinations_with_replacement()将删除未排序的那些,因此您只需要检查是否没有跳过索引。

答案 3 :(得分:0)

在您的具体情况下,您可以简单地选择笛卡尔积产生的那些项目列表的第一个或第二个半部分。

import itertools

alphabet = '01' 
words3Lettered = [''.join(letter) for letter in itertools.product(alphabet,repeat=3)] 

对于n个字母,请使用repeat=n

words3Lettered看起来像这样:

['000', '001', '010', '011', '100', '101', '110', '111']

下,

usefulWords = words3Lettered[:len(words3Lettered)/2]

看起来像这样:

['000', '001', '010', '011']

你可能对另一半感兴趣,即words3Lettered[len(words3Lettered)/2:]虽然另一半应该“折叠”到上半场。

最有可能你想使用数字形式的字母组合,所以......

indexes = [tuple(int(j) for j in word) for word in usefulWords]

给了我们:

[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1)]