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


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.