combinazioni tra due liste?


187

È passato un po 'di tempo e ho problemi a avvolgere la testa attorno a un algoritmo che sto cercando di creare. Fondamentalmente, ho due liste e voglio ottenere tutte le combinazioni delle due liste.

Potrei non spiegarlo correttamente, quindi ecco un esempio.

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

l'output in questo caso sarebbe:

1.  A1 B2
2.  B1 A2

La parte difficile è che potrei avere più elementi nella variabile "name" rispetto agli elementi nella variabile "number" (il numero sarà sempre uguale o inferiore alla variabile name).

Sono confuso su come fare tutte le combinazioni (nidificato per loop?) E ancora più confuso sulla logica per spostare gli elementi nella variabile name nel caso in cui ci siano più elementi nel nome di quelli che sono nell'elenco dei numeri.

Non sono il miglior programmatore, ma penso di poterlo provare se qualcuno può aiutarmi a chiarire la logica / algoritmo per raggiungere questo obiettivo. Quindi sono appena stato bloccato su nidificato per loop.

Aggiornare:

Ecco l'output con 3 variabili e 2 numeri:

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

produzione:

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


1
@ dm03514 L'ho visto, e ho trovato esempi per obiettivi in ​​qualche modo simili usando itertools, ma sto prototipando in Python ma scriverò il codice finale in un'altra lingua, quindi non voglio usare strumenti che non sono disponibili altrove.
user1735075,

1
Quello che stai chiedendo non ha davvero senso. Se il primo elenco contiene A, B, C e il secondo contiene 1,2, quale risultato ti aspetteresti? Potrebbe essere fatto se l'esempio che hai fornito avesse 4 risultati diversi di una lettera e un numero ciascuno (A1, A2, B1, B2) o se entrambe le liste dovessero avere le stesse dimensioni.
interjay,

1
Sono d'accordo con interjay. Specificare il risultato nel caso di dimensioni non uguali, altrimenti non è possibile fornire una soluzione generale.
Bakuriu,

Ciao a tutti, ho aggiornato la risposta per mostrare l'output con 3 nomi e 2 numeri .. Pensavo di averlo spiegato bene, non so perché il downvote.
user1735075,

Risposte:


93

Nota : questa risposta è per la domanda specifica posta sopra. Se sei qui da Google e stai solo cercando un modo per ottenere un prodotto cartesiano in Python, itertools.producto una semplice comprensione dell'elenco potrebbe essere ciò che stai cercando, vedi le altre risposte.


Supponiamo len(list1) >= len(list2). Allora che cosa sembrate volere è quello di prendere tutte le permutazioni di lunghezza len(list2)da list1e abbinarli con gli oggetti da lista2. In pitone:

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

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

ritorna

[[('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
Il risultato è esattamente quello che voglio, ma è possibile condividere la logica dietro a come farlo? Se converto il mio codice in C o Java, non avrei accesso a zip o itertools (anche se rendono la vita molto molto semplice)
user1735075,

3
@ user1735075 Dai un'occhiata alla documentazione
bradipo

1
@ user1735075: sai che Python è open source? Quindi puoi semplicemente scaricare le fonti e vedere cosa fanno. +1 a Mr. Steak per aver sottolineato che la documentazione in realtà ha un'implementazione di esempio che non utilizza zipe simili.
Bakuriu,

2
non riesco letteralmente a farlo funzionare, anche con il tuo esempio ... tutto quello che ottengo è un elenco di oggetti zip ..: |
m1nkeh,

1
@logic fornisce quella che dovrebbe essere la soluzione accettata.
Bernhard Wagner,

502

Il modo più semplice è usare itertools.product:

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

11
OP non chiedeva un prodotto cartesiano e questa risposta (così come la maggior parte degli altri) non fornisce il risultato atteso specificato nella domanda.
interjay

17
@interjay hai ragione, ma poiché troppe persone sembrano trovare questa risposta corretta, posso solo supporre che il titolo della domanda sia privo di contesto.
xpy,

3
@xpy Il titolo è troppo breve per spiegare tutto. Ecco perché devi leggere la vera domanda.
interjay

10
OP ha voluto permessi, ma Google invia chiunque cerchi combinazioni (come me) a questa risposta - felice di vedere che ha 8 volte i voti!
Josh Friedlander,

160

Potrebbe essere più semplice di quello più semplice sopra:

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

senza alcuna importazione


Soluzione migliore! Grazie! Altre soluzioni sono semplicemente sbagliate o funzionano solo in casi specifici come a> b ecc.
Philipp Schwarz,

3
La maggior parte della soluzione Pythonic! (ed evita importazioni inutili)
Dalker

6
La complessità temporale è O (n ^ 2)
Deepak Sharma il

2
Soluzione di scommesse !! Le basi nude sono sempre il modo migliore
Sabyasachi il

22

Stavo cercando un elenco moltiplicato da solo con solo combinazioni uniche, che viene fornito come questa funzione.

import itertools
itertools.combinations(list, n_times)

Qui come estratto dai documenti di Python su itertools Ciò potrebbe aiutarti a trovare quello che stai cercando.

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

Potresti provare una comprensione dell'elenco di una riga:

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

il modo migliore per scoprire tutte le combinazioni per un gran numero di liste è:

import itertools
from pprint import pprint

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

il risultato sarà:

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

Grazie, ottima risposta!
toinbis,

10

O la risposta KISS per gli elenchi brevi:

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

Non performante come gli itertools ma stai usando Python quindi le prestazioni non sono già la tua preoccupazione principale ...

Mi piacciono anche tutte le altre risposte!


8

un piccolo miglioramento per la risposta di interjay, per rendere il risultato un elenco appiattito.

>>> 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)]

riferimento da questo link


4

Senza itertools

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

4

Rispondendo alla domanda "dati due elenchi, trova tutte le possibili permutazioni di coppie di un elemento da ciascun elenco" e usando la funzionalità di base di Python (cioè senza itertools) e, quindi, facilitando la replica per altri linguaggi di programmazione:

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)

ritorna

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

2

Le risposte migliori a questo funzionano solo per lunghezze specifiche di elenchi forniti.

Ecco una versione che funziona per qualsiasi lunghezza di input. Rende inoltre chiaro l'algoritmo in termini di concetti matematici di combinazione e permutazione.

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}')

Questo produce:

0: A1 B2
1: A1 C2
2: B1 A2
3: B1 C2
4: C1 A2
5: C1 B2
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.