我想生成将列表中的索引与“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
答案 0 :(得分:2)
我首先要做出以下观察:
这些观察结果表明了以下算法:
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)]