combinaciones entre dos listas?


187

Ha pasado un tiempo y estoy teniendo problemas para entender un algoritmo que intento hacer. Básicamente, tengo dos listas y quiero obtener todas las combinaciones de las dos listas.

Puede que no lo esté explicando correctamente, así que aquí hay un ejemplo.

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

La salida en este caso sería:

1.  A1 B2
2.  B1 A2

La parte difícil es que podría tener más elementos en la variable "nombre" que elementos en la variable "número" (el número siempre será igual o menor que la variable nombre).

Estoy confundido sobre cómo hacer todas las combinaciones (¿anidado para bucle?) Y aún más confundido sobre la lógica para cambiar los elementos en la variable de nombre en caso de que haya más elementos en el nombre de los que están en la lista de números.

No soy el mejor programador, pero creo que puedo intentarlo si alguien puede ayudarme a aclarar la lógica / algoritmo para lograr esto. Así que me he quedado atascado en bucles anidados.

Actualizar:

Aquí está la salida con 3 variables y 2 números:

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

salida:

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


1
@ dm03514 Vi eso y encontré ejemplos para objetivos algo similares usando itertools pero estoy creando prototipos en python pero escribiré el código final en otro idioma, así que no quiero usar ninguna herramienta que no esté disponible de otra manera.
user1735075

1
Lo que estás pidiendo realmente no tiene sentido. Si la primera lista contiene A, B, C y la segunda contiene 1,2, ¿qué resultado esperarías? Se podría hacer si el ejemplo que dio tiene 4 resultados diferentes de una letra y un número cada uno (A1, A2, B1, B2), o si ambas listas tienen que tener el mismo tamaño.
Interjay

1
Estoy de acuerdo con el interjay. Especifique el resultado en el caso de tamaño no igual, de lo contrario no es posible proporcionar una solución general.
Bakuriu

Hola a todos, actualicé la respuesta para mostrar el resultado con 3 nombres y 2 números ... Pensé que lo había explicado bien, no estoy seguro de por qué el voto negativo.
user1735075

Respuestas:


93

Nota : Esta respuesta es para la pregunta específica que se hizo anteriormente. Si está aquí desde Google y solo está buscando una forma de obtener un producto cartesiano en Python, itertools.producto una simple lista de comprensión puede ser lo que está buscando, vea las otras respuestas.


Supongamos len(list1) >= len(list2). Entonces lo que parecen querer es tomar todas las permutaciones de longitud len(list2)desde list1y relacionarlos con artículos de lista2. En python:

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

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

Devoluciones

[[('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
El resultado es exactamente lo que quiero, pero ¿es posible compartir la lógica detrás de cómo hacerlo? Si convierto mi código a C o Java, no tendré acceso a zip o itertools (aunque hacen la vida muy fácil)
user1735075

3
@ user1735075 Echa un vistazo a la documentación
perezoso

1
@ user1735075: ¿sabes que Python es de código abierto? Así que simplemente puede descargar las fuentes y ver qué hacen. +1 al Sr. Steak por señalar que la documentación en realidad tiene una implementación de muestra que no utiliza zipy similar.
Bakuriu

2
Literalmente no puedo hacer que esto funcione, incluso con su ejemplo ... todo lo que obtengo es una lista de objetos zip ...: |
m1nkeh

1
@logic proporciona lo que debería ser la solución aceptada.
Bernhard Wagner

502

La forma más simple es usar itertools.product:

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

11
OP no estaba pidiendo un producto cartesiano, y esta respuesta (así como la mayoría de las otras) no da el resultado esperado especificado en la pregunta.
interjay

17
@interjay tienes razón, pero como muchas personas parecen encontrar esta respuesta como correcta, entonces solo puedo suponer que el título de la pregunta carece de contexto.
xpy

3
@xpy El título es demasiado corto para explicarlo todo. Es por eso que necesitas leer la pregunta real.
Interjay

10
OP quería permutaciones, pero Google envía a cualquiera que busque combinaciones (como yo) a esta respuesta, ¡me alegra ver que tiene 8 veces más votos!
Josh Friedlander

160

Puede ser más simple que el más simple de arriba:

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

sin ninguna importación


¡Mejor solución! ¡Gracias! Otras soluciones son simplemente incorrectas o solo funcionan en casos específicos como a> b etc.
Philipp Schwarz

3
¡La solución más pitónica! (y evita importaciones innecesarias)
Dalker

66
La complejidad del tiempo es O (n ^ 2)
Deepak Sharma el

2
¡Solución de apuestas! Lo básico es la mejor manera siempre
Sabyasachi

22

Estaba buscando una lista multiplicada por sí misma con solo combinaciones únicas, que se proporciona como esta función.

import itertools
itertools.combinations(list, n_times)

Aquí como un extracto de los documentos de Python sobre itertools Eso podría ayudarte a encontrar lo que estás buscando.

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

Es posible que desee probar una comprensión de la lista de una línea:

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

La mejor manera de descubrir todas las combinaciones para un gran número de listas es:

import itertools
from pprint import pprint

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

el resultado será:

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

Gracias, gran respuesta!
toinbis

10

O la respuesta de KISS para listas cortas:

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

No es tan eficaz como las herramientas iterto, pero está utilizando Python, por lo que el rendimiento ya no es su principal preocupación ...

¡Me gustan todas las otras respuestas también!


8

Una pequeña mejora para la respuesta del interjay, para hacer que el resultado sea una lista plana.

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

referencia desde este enlace


4

Sin itertools

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

4

Respondiendo a la pregunta "dadas dos listas, encuentre todas las permutaciones posibles de pares de un elemento de cada lista" y utilizando la funcionalidad básica de Python (es decir, sin herramientas iterativas) y, por lo tanto, facilitando la replicación para otros lenguajes de programación:

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)

Devoluciones

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

2

Las mejores respuestas a esto solo funcionan para longitudes específicas de las listas que se proporcionan.

Aquí hay una versión que funciona para cualquier longitud de entrada. También aclara el algoritmo en términos de los conceptos matemáticos de combinación y permutación.

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

Esto produce:

0: A1 B2
1: A1 C2
2: B1 A2
3: B1 C2
4: C1 A2
5: C1 B2
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.