2011-02-05 15 views
5

que tienen dos grupos de opciones:Prueba todas las combinaciones en Python

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

El número de elementos en los optionlists no son necesariamente iguales y tengo que elegir entre el primer listaOpciones dos veces. ¿Cómo me aseguro de haber probado todas las combinaciones de 2 opciones de la primera lista y una de la segunda lista? Una selección de ejemplo establecida a continuación ...

selectedOptions = [an1,an2,bn] 
+1

lo siento, pero no estoy muy claro sobre lo que estás tratando de hacer. ¿Necesita una tabla de todas las combinaciones posibles de elementos del elemento, donde ninguno es de la misma lista? ¿Debería algún nodo ser None? – fncomp

+0

Supongo que tardé en descubrir lo que querías. – fncomp

Respuesta

6

Suponiendo que usted no desea que las entradas duplicadas de lista1, aquí es un generador que se puede utilizar para iterar sobre todas las combinaciones:

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

Esto, sin embargo, no selecciona las mismas opciones de list1 en diferentes ordenamientos. Si desea obtener tanto [a1, a2, b1] y [A2, A1, B1] entonces usted puede utilizar:

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

En términos de Python, un generador es algo que usa 'rendimiento'. Éste tiene la desventaja de almacenar toda la lista con opciones en la memoria. –

+1

Y, por supuesto, el OP puede llamarlo con los dos primeros parámetros siendo la misma lista. combinaciones (optionList1, optionList1, optionList2) en caso de que no esté claro. – Spacedman

+1

La función realmente devuelve una expresión del generador, por lo que no genera toda la lista en la memoria. – shang

2

Una forma de hacerlo es usar itertools.product.

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

Esto no prueba todas las combinaciones. En cambio, combina los índices en la lista, solo devuelve (a1, a1, b1), (a2, a2, b2) .... (an, an, bn) si ambos tuvieran la misma longitud. –

+0

@Peter.Sí, solo lo estaba actualizando con la respuesta correcta. Encuentro que ha señalado la respuesta correcta. –

+0

No necesita agregar la lista con valores vacíos. itertools.product() puede manejar listas (o iteradores) con diferentes longitudes. –

6

Puede utilizar itertools.product para esto. Devuelve todas las combinaciones posibles.

Por ejemplo

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

Esto producirá "dobles" como [a1, a1, b2] y [A2, A3, B2], [a3, a2, b2]. Puedes arreglar esto con un filtro. A continuación se previene cualquier dobles *:

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

(*) Esto asume que las opciones tienen algún ordenamiento natural que será el caso con todos los valores primitivos.

shang 's answer también es muy bueno. Escribí algo de código para compararlos:

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 

y el resultado es:

0.189330005646 
0.428138256073 

lo tanto, el método de generador es más rápido. Sin embargo, la velocidad no es todo. Personalmente encuentro mi código 'más limpio', ¡pero la elección es tuya!

(Por cierto, dan ambos casos idénticos, por lo que ambos son igualmente correctas.)

3

Me suena como que está buscando 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

No estoy seguro de entender: por lo que yo puedo decir, esto funciona de la misma manera, independientemente de la longitud de cualquiera de las listas. En cuanto a si responde a la pregunta, es difícil de decir, ya que la pregunta en sí era un tanto ambigua: el OP no especificó si se incluirán grupos de selecciones de opciones con elementos duplicados del primer conjunto de opciones. Debido a esta ambigüedad, no puedo afirmar que esta sea * precisamente * la solución al problema del OP, pero decir que "ni siquiera se acerca" parece bastante extremo. –

6

Combinar product y permutations de itertools suponiendo que no quiero duplicados de la primera lista:

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

¡¡¡Buena solución !! –

+1

¡Muy bonito! También tenga en cuenta que puede reemplazar 'permutations' con' combinations' si no desea pares repetidos con diferente orden. – shang

+0

@shang, lo tenía originalmente pero no estaba claro qué quería el OP. Pensé que las permutaciones eran más probables. OP podría estar bien con duplicados, también, entonces 'product (o1, o1, o2)' estaría bien. –

Cuestiones relacionadas