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

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
user1735075
источник
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)]]
interjay
источник
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)]
DrIDK
источник
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
ThorSummoner
источник
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']
Idanmel
источник
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, так что производительность уже не ваша главная задача ...

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

Fletch F Fletch
источник
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

Без itertools

[(list1[i], list2[j]) for i in xrange(len(list1)) for j in xrange(len(list2))]
user3684792
источник
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']]
computerist
источник
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
Стив Александр
источник