使用python,我尝试通过仅更改列表中的特定元素来查找列表的所有组合。例如,如果我有一个列表[1,2,3,4,5,6,7,8,9]
并向其添加3个字符串,那么我将拥有:
[1,2,3,4,5,6,7,8,9,'string','string','string']
然后我想查找仅允许更改字符串位置时列表可以采用的所有组合。
例如
[1,2,3,4,5,'string',6,7,8,9,'string','string']
[1,2,'string',3,4,5,'string',6,7,'string',8,9]
['string',1,'string',2,3,4,'string',5,6,7,8,9]
等,同时仍以相同的升序保留原始数字列表。 我并不一定要一次存储每个简单的组合,而只是尝试类似的事情:
我一直试图找到一种解决方案,而不必使用不必要的for循环,并且该解决方案将适用于可能附加了更多字符串的较大列表。我一直在寻找使用itertools的方法,但似乎找不到方法。
我发现一个解决方案可能是只使用itertools.permutations
(带有附加字符串的列表),然后使用条件检查数字是否按升序排列,但是我担心这种方法的确效率低下并且会占用会增加内存,特别是在处理较大的列表时。
我们将不胜感激,在此先感谢您。
答案 0 :(得分:3)
我想您可以在@wjandrea的注释行中做些事情:
from itertools import combinations, permutations
lst = [1,2,3,4,5,6,7,8,9]
strings = ['foo', 'bar', 'foobar']
for positions in combinations(range(len(lst) + len(strings)), len(strings)):
for permutation in permutations(strings, len(strings)):
cop = lst[:]
for string, pos in zip(permutation, positions):
cop.insert(pos, string)
print(cop)
输出(小样本)
['foo', 'foobar', 1, 2, 3, 'bar', 4, 5, 6, 7, 8, 9]
['bar', 'foo', 1, 2, 3, 'foobar', 4, 5, 6, 7, 8, 9]
['bar', 'foobar', 1, 2, 3, 'foo', 4, 5, 6, 7, 8, 9]
['foobar', 'foo', 1, 2, 3, 'bar', 4, 5, 6, 7, 8, 9]
['foobar', 'bar', 1, 2, 3, 'foo', 4, 5, 6, 7, 8, 9]
['foo', 'bar', 1, 2, 3, 4, 'foobar', 5, 6, 7, 8, 9]
['foo', 'foobar', 1, 2, 3, 4, 'bar', 5, 6, 7, 8, 9]
请注意,此解决方案假定您可以重新排序字符串。
答案 1 :(得分:2)
@DanielMesejo的答案有效,但使用list.insert
方法将每个字符串插入其在数字列表副本中的位置,由于list.insert
的平均时间复杂度为 O,因此效率低下(n)每次迭代。
根据位置和字符串排列的组合来构造每个列表的更有效方法是使用dict,将位置映射到字符串,然后在整个长度上迭代该位置,如果该位置在dict,在该位置输出字符串;否则,将使用迭代器输出列表中的下一个数字。
为了获得更高的效率,您可以在itertools.product
和combinations
的两个生成器上使用permutation
来避免使用嵌套循环来重复计算相同置换的麻烦:
from itertools import combinations, permutations, product
lst = [1,2,3,4,5,6,7,8,9]
strings = ['foo', 'bar', 'foobar']
total_len = len(lst) + len(strings)
for positions, permutation in product(combinations(range(total_len), len(strings)),
permutations(strings, len(strings))):
string_pos = dict(zip(positions, permutation))
numbers = iter(lst)
print([string_pos[i] if i in string_pos else next(numbers) for i in range(total_len)])
答案 2 :(得分:1)
您可以将递归与生成器一起使用:
data, s = [1,2,3,4,5,6,7,8,9], ['foo', 'bar', 'foobar']
def groups(d):
if all(i in d for i in s):
yield d
else:
for i in range(len(d)):
for k in filter(lambda x:x not in d, s):
yield from groups(d[:i]+[k]+d[i:])
result = [*set(map(tuple, groups(data)))]
输出:
[(1, 2, 3, 4, 'foo', 'foobar', 5, 'bar', 6, 7, 8, 9),
(1, 'foo', 'foobar', 2, 'bar', 3, 4, 5, 6, 7, 8, 9),
(1, 'foobar', 2, 3, 'bar', 4, 5, 6, 'foo', 7, 8, 9),
('bar', 'foo', 1, 2, 'foobar', 3, 4, 5, 6, 7, 8, 9),
(1, 2, 'foobar', 3, 4, 5, 6, 'bar', 'foo', 7, 8, 9),
(1, 2, 3, 'foo', 'foobar', 4, 5, 6, 'bar', 7, 8, 9),
(1, 'bar', 2, 3, 'foo', 4, 5, 6, 7, 'foobar', 8, 9),
(1, 2, 3, 'foobar', 4, 5, 'bar', 6, 7, 'foo', 8, 9),
(1, 2, 3, 'foo', 4, 5, 6, 'foobar', 'bar', 7, 8, 9),
(1, 'foobar', 2, 3, 4, 'bar', 5, 'foo', 6, 7, 8, 9)
...
]
答案 3 :(得分:1)
您可以通过回溯来做到这一点。在路径的每个步骤中,我们都可以附加数字或字符串(因此,路径是一系列决策)。如果到达路径的末尾,则将该路径的副本附加到结果中。
我们可以将此方法应用于strings
列表的所有排列,以获得最终答案:
from itertools import permutations
def combinations(nums, strings):
res = []
def generate(i, j, string_combo, path):
if i == len(nums) and j == len(strings): # path ends
res.append(path[:])
return
if i < len(nums): # choose number
path.append(nums[i])
generate(i + 1, j, string_combo, path)
path.pop()
if j < len(strings): # choose string
path.append(string_combo[j])
generate(i, j + 1, string_combo, path)
path.pop()
for p in permutations(strings):
generate(0, 0, p, [])
return res
result = combinations([1, 2, 3, 4, 5, 6, 7, 8, 9], ['A', 'B', 'C'])
print(len(result)) # 1320
此方法的时间复杂度为 O(s!(n + s)2 (n + s))。这是因为字符串列表存在 s!个排列,并且对于每个排列,我们都会回溯。每个路径的长度为(n + s),我们每步执行2次操作,最后进行一次复制。