комбінації між двома списками?


187

Минув час, і у мене виникають проблеми з обернути голову навколо алгоритму, який я намагаюся зробити. В основному я маю два списки і хочу отримати всі комбінації двох списків.

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

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

вихід у цьому випадку буде:

1.  A1 B2
2.  B1 A2

Складна частина полягає в тому, що у мене може бути більше елементів у змінній "ім'я", ніж елементів у змінній "число" (число завжди буде рівним або меншим змінної імені).

Мене бентежить, як зробити всі комбінації (вкладені в цикл?) І ще більше заплутався в логіці переміщення елементів у змінній імен у випадку, якщо в імені є більше елементів, ніж їх у списку номерів.

Я не найкращий програміст, але думаю, що я можу спробувати, якщо хтось може допомогти мені з’ясувати логіку / алгоритм для досягнення цього. Так що я щойно застряг на вкладених петлях.

Оновлення:

Ось результат з 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, але буду писати остаточний код на іншій мові, тому я не хочу використовувати будь-які інструменти, які не використовуються в іншому випадку.
користувач1735075

1
Те, що ви просите, насправді не має сенсу. Якщо перший список містить A, B, C, а другий містить 1,2, який результат ви б очікували? Це можна зробити, якби приклад, який ви подали, мав 4 різні результати по одній букві і по одному номеру кожен (A1, A2, B1, B2) або якщо обидва списки мали мати однаковий розмір.
interjay

1
Я згоден з interjay. Будь ласка, вкажіть результат у випадку нерівного розміру, інакше загальне рішення неможливо.
Бакуріу

Привіт усім, я оновив відповідь, щоб показати вихід з 3-ма іменами та 2 числами. Я думав, що я це добре пояснив, не знаю, чому зворотний результат.
користувач1735075

Відповіді:


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
я буквально не можу змусити це працювати, навіть з вашим прикладом ... все, що я отримую, - це список об'єктів zip ..: |
m1nkeh

1
@logic забезпечує те, що має бути прийнятим рішенням.
Бернхард Вагнер

500

Найпростіший спосіб - це використовувати itertools.product:

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

11
ОП не просила декартового продукту, і ця відповідь (як і більшість інших) не дає очікуваного результату, зазначеного в питанні.
interjay

17
@interjay Ви дуже праві, але оскільки занадто багато людей здаються цією відповіддю правильною, то я можу лише припустити, що в заголовку питання не вистачає контексту.
xpy

3
@xpy Назва занадто коротка, щоб усе пояснити. Ось чому вам потрібно прочитати власне питання.
interjay

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 тощо.
Philipp Schwarz

3
Найбільше пітонічне рішення! (і уникає зайвого імпорту)
Далкер

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

крихітне покращення для відповіді від інтержа, щоб зробити результат як сплющений список.

>>> 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.