2011-02-05 23 views
5

Ho due gruppi di opzioni:test tutte le combinazioni in Python

optionList1 = [a1,a2,a3,...,an] 
optionList2 = [b1,b2,b3,...,bn] 

Il numero degli elementi nelle optionlists non sono necessariamente uguali e devo scegliere tra la prima optionlist due volte. Come posso assicurarmi di aver provato ogni combinazione di 2 opzioni dalla prima lista e una dalla seconda lista. Un esempio di selezione impostato sotto ...

selectedOptions = [an1,an2,bn] 
+1

scusa, ma sono un po 'incerto su cosa stai cercando di fare. Hai bisogno di una tabella di tutte le possibili combinazioni di elementi dell'elemento, dove nessuno proviene dalla stessa lista? Qualsiasi nodo dovrebbe mai essere None? – fncomp

+0

Immagino di essere stato lento a capire cosa volevi. – fncomp

risposta

6

Supponendo che non si desidera voci duplicate da lista1, ecco un generatore che è possibile utilizzare per iterare su tutte le combinazioni:

def combinations(list1, list2): 
    return ([opt1, opt2, opt3] 
      for i,opt1 in enumerate(list1) 
      for opt2 in list1[i+1:] 
      for opt3 in list2) 

Questo, tuttavia, non seleziona le stesse opzioni da lista1 in diversi ordini. Se si vuole ottenere sia [a1, a2, b1] e [a2, a1, b1] allora si può usare:

def combinations(list1, list2): 
    return ([opt1, opt2, opt3] 
      for opt1 in list1 
      for opt2 in list1 
      for opt3 in list2 if opt1 != opt2) 
+2

In termini Python, un generatore è qualcosa che usa il 'yield'. Questo ha lo svantaggio di memorizzare l'intera lista con le opzioni in memoria. –

+1

E naturalmente l'OP può chiamare questo con i primi due parametri che sono la stessa lista. combinazioni (optionList1, optionList1, optionList2) nel caso in cui non sia chiaro. – Spacedman

+1

La funzione restituisce effettivamente un'espressione generatore, quindi non crea l'intera lista in memoria. – shang

2

Un modo per fare è utilizzare itertools.product.

for x, y, z in itertools.product(optionlist1,optionlist1,optionlist2): 
    print x,y,z 
+0

Questo non prova tutte le combinazioni. Invece corrisponda gli indici nella lista, solo il ritorno (a1, A1, B1), (a2, a2, b2) .... (un, an, bn) se entrambi sarebbe stessa lunghezza. –

+0

@Peter.Sì, stavo solo aggiornandolo con la risposta corretta. Trovo che tu abbia indicato la risposta corretta. –

+0

Non è necessario aggiungere l'elenco con valori vuoti. itertools.product() può gestire liste (o iteratori) con diverse lunghezze. –

6

È possibile utilizzare itertools.product per questo. Restituisce tutte le possibili combinazioni.

Ad esempio

for a1, a2, b in itertools.product(optionlist1,optionlist1,optionlist2): 
    do_something(a1,a2,b) 

Ciò produrrà "doppi" come [a1, a1, b2] e [a2, a3, b2], [a3, a2, b2]. Puoi sistemarlo con un filtro. Di seguito le impedisce eventuali doppie *:

for a1,a2,b in itertools.ifilter(lambda x: x[0]<x[1], itertools.product(optionlist1,optionlist1,optionlist2)): 
    do_something(a1,a2,b) 

(*) Questo presuppone che le opzioni hanno qualche ordinamento naturale che sarà il caso di tutti i valori di base.

shang 's answer è anche molto buono. Ho scritto un codice per confrontarli:

from itertools import ifilter, product 
import random 
from timeit import repeat 

def generator_way(list1, list2): 
    def combinations(list1, list2): 
     return ([opt1, opt2, opt3] 
       for i,opt1 in enumerate(list1) 
       for opt2 in list1[i+1:] 
       for opt3 in list2) 
    count = 0 
    for a1,a2,b in combinations(list1,list2): 
     count += 1 

    return count 

def itertools_way(list1,list2): 
    count = 0 
    for a1,a2,b in ifilter(lambda x: x[0] < x[1], product(list1,list1,list2)): 
     count += 1 
    return count 

list1 = range(0,100) 
random.shuffle(list1) 
list2 = range(0,100) 
random.shuffle(list2) 

print sum(repeat(lambda: generator_way(list1,list2),repeat = 10, number=1))/10 
print sum(repeat(lambda: itertools_way(list1,list2),repeat = 10, number=1))/10 

e il risultato è:

0.189330005646 
0.428138256073 

Quindi il metodo generatore è più veloce. Tuttavia, la velocità non è tutto. Personalmente trovo il mio codice 'più pulito', ma la scelta è tua!

(Btw, danno entrambi i fronti identici, quindi entrambi sono ugualmente corrette.)

3

Sembra a me come siete alla ricerca di itertools.product()

>>> options_a = [1,2] 
>>> options_b = ['a','b','c'] 
>>> list(itertools.product(options_a, options_a, options_b)) 
[(1, 1, 'a'), 
(1, 1, 'b'), 
(1, 1, 'c'), 
(1, 2, 'a'), 
(1, 2, 'b'), 
(1, 2, 'c'), 
(2, 1, 'a'), 
(2, 1, 'b'), 
(2, 1, 'c'), 
(2, 2, 'a'), 
(2, 2, 'b'), 
(2, 2, 'c')] 
+0

Non sono sicuro di aver capito: per quanto posso dire, questo funziona allo stesso modo indipendentemente dalla lunghezza di una delle due liste. Per quanto riguarda la risposta alla domanda, è difficile dirlo, poiché la domanda in sé era alquanto ambigua: l'OP non ha specificato se includere gruppi di selezioni di opzioni con elementi duplicati dal primo gruppo di opzioni. A causa di questa ambiguità, non posso affermare che questa sia * precisamente * la soluzione al problema dell'OP, ma dire che "non si avvicina nemmeno" sembra piuttosto estremo. –

6

Unire product e permutations da itertools supponendo non voglio duplicati dal primo elenco:

>>> from itertools import product,permutations 
>>> o1 = 'a1 a2 a3'.split() 
>>> o2 = 'b1 b2 b3'.split() 
>>> for (a,b),c in product(permutations(o1,2),o2): 
...  print a,b,c 
... 
a1 a2 b1 
a1 a2 b2 
a1 a2 b3 
a1 a3 b1 
a1 a3 b2 
a1 a3 b3 
a2 a1 b1 
a2 a1 b2 
a2 a1 b3 
a2 a3 b1 
a2 a3 b2 
a2 a3 b3 
a3 a1 b1 
a3 a1 b2 
a3 a1 b3 
a3 a2 b1 
a3 a2 b2 
a3 a2 b3 
+0

Bella soluzione !! –

+1

Molto bello! Nota anche che puoi sostituire 'permutations' con' combination' se non vuoi coppie ripetute con ordini diversi. – shang

+0

@shang, l'avevo in origine ma non era chiaro cosa volesse l'OP. Pensavo che le permutazioni fossero più probabili. OP potrebbe andare bene anche con duplicati, quindi 'prodotto (o1, o1, o2)' andrebbe bene. –

Problemi correlati