комбинации между двумя списками?


187

Прошло какое-то время, и у меня возникли проблемы с нахождением алгоритма, который я пытаюсь сделать. По сути, у меня есть два списка, и я хочу получить все комбинации из двух списков.

Я не могу объяснить это правильно, так что вот пример.

name = 'a', 'b'
number = 1, 2

выход в этом случае будет:

1.  A1 B2
2.  B1 A2

Сложность в том, что у меня может быть больше элементов в переменной «name», чем элементов в переменной «number» (число всегда будет равно или меньше переменной name).

Я запутался, как делать все комбинации (вложенные для цикла?), И еще больше запутался в логике, чтобы сместить элементы в переменной name в случае, если в имени больше элементов, чем в списке номеров.

Я не лучший программист, но думаю, что смогу дать ему шанс, если кто-нибудь поможет мне прояснить логику / алгоритм для достижения этой цели. Так что я только что застрял на вложенных для петель.

Обновить:

Вот результат с 3 переменными и 2 числами:

name = 'a', 'b', 'c'
number = 1, 2

вывод:

1.  A1 B2
2.  B1 A2
3.  A1 C2
4.  C1 A2
5.  B1 C2
6.  C1 B2


1
@ dm03514 Я видел это и нашел примеры для несколько схожих целей, используя itertools, но я создаю прототипы на python, но напишу окончательный код на другом языке, поэтому я не хочу использовать какие-либо инструменты, которые не доступны в других случаях.
— user1735075

1
То, что вы просите, на самом деле не имеет смысла. Если первый список содержит A, B, C, а второй содержит 1,2, какого результата вы ожидаете? Это можно сделать, если приведенный вами пример имеет 4 разных результата по одной букве и по одному номеру (A1, A2, B1, B2) или если оба списка должны иметь одинаковый размер.
— междурядное

1
Я согласен с interjay. Пожалуйста, укажите результат в случае неравного размера, иначе невозможно дать общее решение.
— Бакуриу

Привет всем, я обновил ответ, чтобы показать вывод с 3 именами и 2 числами .. Я думал, что объяснил это хорошо, не уверен, почему понизить голос.
— user1735075

Ответы:


93

Примечание . Этот ответ предназначен для конкретного вопроса, заданного выше. Если вы находитесь здесь от Google и просто ищете способ получить декартово произведение на Python, itertools.productили вам может понадобиться простое понимание списка - посмотрите другие ответы.


Пусть len(list1) >= len(list2). Тогда то , что вы , кажется, хотят, чтобы принять все перестановки длины len(list2)от list1и сопоставить их с элементами из list2. В питоне:

import itertools
list1=['a','b','c']
list2=[1,2]

[list(zip(x,list2)) for x in itertools.permutations(list1,len(list2))]

Возвращает

[[('a', 1), ('b', 2)], [('a', 1), ('c', 2)], [('b', 1), ('a', 2)], [('b', 1), ('c', 2)], [('c', 1), ('a', 2)], [('c', 1), ('b', 2)]]

1
Результат именно то, что я хочу, но можно ли поделиться логикой, как это сделать? Если я преобразую свой код в C или Java, у меня не будет доступа к zip или itertools (хотя они очень упрощают жизнь)
— user1735075

3
@ user1735075 Посмотрите документацию
— ленивец

1
@ user1735075: знаете ли вы, что Python с открытым исходным кодом? Так что вы можете просто скачать исходники и посмотреть, что они делают. +1 г-ну Стейку за то, что он указал, что документация на самом деле имеет пример реализации, который не использует zipи похожий.
— Бакуриу

2
я буквально не могу заставить это работать, даже с вашим примером ... все, что я получаю, является списком объекта почтового индекса ..: |
— m1nkeh

1
@logic предоставляет то, что должно быть принятым решением.
— Бернхард Вагнер

502

Самый простой способ - использовать itertools.product:

a = ["foo", "melon"]
b = [True, False]
c = list(itertools.product(a, b))
>> [("foo", True), ("foo", False), ("melon", True), ("melon", False)]

11
ОП не запрашивал декартово произведение, и этот ответ (как и большинство других) не дает ожидаемого результата, указанного в вопросе.
— междурядное

17
@interjay, вы очень правы, но слишком много людей считают этот ответ правильным, поэтому я могу только предположить, что в названии вопроса отсутствует контекст.
— xpy

3
@xpy Название слишком короткое, чтобы объяснить все. Вот почему вам нужно прочитать актуальный вопрос.
— междурядье

10
ОП хотел перестановок, но Google отправляет всех, кто ищет комбинации (как я), на этот ответ - рад видеть, что он получил в 8 раз больше голосов!
— Джош Фридлендер

160

Может быть проще, чем простой выше:

>>> a = ["foo", "bar"]
>>> b = [1, 2, 3]
>>> [(x,y) for x in a for y in b]  # for a list
[('foo', 1), ('foo', 2), ('foo', 3), ('bar', 1), ('bar', 2), ('bar', 3)]
>>> ((x,y) for x in a for y in b)  # for a generator if you worry about memory or time complexity.
<generator object <genexpr> at 0x1048de850>

без какого-либо импорта


Лучшее решение! Спасибо! Другие решения либо просто неверны, либо работают только в особых случаях, таких как> b и т. Д.
— Филипп Шварц,

3
Самое питонское решение! (и избегает ненужного импорта)
— Dalker

6
Сложность времени O (n ^ 2)
— Дипак Шарма

2
Ставки решение !! Голые основы это лучший способ всегда
— Sabyasachi

22

Я искал список, умноженный на себя только с уникальными комбинациями, который предоставляется как эта функция.

import itertools
itertools.combinations(list, n_times)

Вот отрывок из документации по Python, itertools который поможет вам найти то, что вы ищете.

Combinatoric generators:

Iterator                                 | Results
-----------------------------------------+----------------------------------------
product(p, q, ... [repeat=1])            | cartesian product, equivalent to a 
                                         |   nested for-loop
-----------------------------------------+----------------------------------------
permutations(p[, r])                     | r-length tuples, all possible 
                                         |   orderings, no repeated elements
-----------------------------------------+----------------------------------------
combinations(p, r)                       | r-length tuples, in sorted order, no 
                                         |   repeated elements
-----------------------------------------+----------------------------------------
combinations_with_replacement(p, r)      | r-length tuples, in sorted order, 
                                         | with repeated elements
-----------------------------------------+----------------------------------------
product('ABCD', repeat=2)                | AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD
permutations('ABCD', 2)                  | AB AC AD BA BC BD CA CB CD DA DB DC
combinations('ABCD', 2)                  | AB AC AD BC BD CD
combinations_with_replacement('ABCD', 2) | AA AB AC AD BB BC BD CC CD DD

11

Возможно, вы захотите попробовать понимание списка в одну строку:

>>> [name+number for name in 'ab' for number in '12']
['a1', 'a2', 'b1', 'b2']
>>> [name+number for name in 'abc' for number in '12']
['a1', 'a2', 'b1', 'b2', 'c1', 'c2']

11

Лучший способ узнать все комбинации для большого количества списков:

import itertools
from pprint import pprint

inputdata = [
    ['a', 'b', 'c'],
    ['d'],
    ['e', 'f'],
]
result = list(itertools.product(*inputdata))
pprint(result)

результат будет:

[('a', 'd', 'e'),
 ('a', 'd', 'f'),
 ('b', 'd', 'e'),
 ('b', 'd', 'f'),
 ('c', 'd', 'e'),
 ('c', 'd', 'f')]

Спасибо, отличный ответ!
— Toinbis

10

Или ответ KISS для коротких списков:

[(i, j) for i in list1 for j in list2]

Не так быстро, как itertools, но вы используете Python, так что производительность уже не ваша главная задача ...

Мне также нравятся все остальные ответы!


8

крошечное улучшение для ответа от interjay, чтобы сделать результат в виде сплющенного списка.

>>> list3 = [zip(x,list2) for x in itertools.permutations(list1,len(list2))]
>>> import itertools
>>> chain = itertools.chain(*list3)
>>> list4 = list(chain)
[('a', 1), ('b', 2), ('a', 1), ('c', 2), ('b', 1), ('a', 2), ('b', 1), ('c', 2), ('c', 1), ('a', 2), ('c', 1), ('b', 2)]

ссылка по этой ссылке



4

Отвечая на вопрос «дано два списка, найдите все возможные перестановки пар по одному элементу из каждого списка» и используя базовые функциональные возможности Python (т.е. без itertools) и, следовательно, упростите репликацию для других языков программирования:

def rec(a, b, ll, size):
    ret = []
    for i,e in enumerate(a):
        for j,f in enumerate(b):
            l = [e+f]
            new_l = rec(a[i+1:], b[:j]+b[j+1:], ll, size)
            if not new_l:
                ret.append(l)
            for k in new_l:
                l_k = l + k
                ret.append(l_k)
                if len(l_k) == size:
                    ll.append(l_k)
    return ret

a = ['a','b','c']
b = ['1','2']
ll = []
rec(a,b,ll, min(len(a),len(b)))
print(ll)

Возвращает

[['a1', 'b2'], ['a1', 'c2'], ['a2', 'b1'], ['a2', 'c1'], ['b1', 'c2'], ['b2', 'c1']]

2

Лучшие ответы на это работают только для определенных длин списков, которые предоставляются.

Вот версия, которая работает для любой длины ввода. Это также делает алгоритм понятным с точки зрения математических концепций комбинации и перестановки.

from itertools import combinations, permutations
list1 = ['1', '2']
list2 = ['A', 'B', 'C']

num_elements = min(len(list1), len(list2))
list1_combs = list(combinations(list1, num_elements))
list2_perms = list(permutations(list2, num_elements))
result = [
  tuple(zip(perm, comb))
  for comb in list1_combs
  for perm in list2_perms
]

for idx, ((l11, l12), (l21, l22)) in enumerate(result):
  print(f'{idx}: {l11}{l12} {l21}{l22}')

Это выводит:

0: A1 B2
1: A1 C2
2: B1 A2
3: B1 C2
4: C1 A2
5: C1 B2
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.