combinações entre duas listas?


187

Já faz um tempo e estou tendo problemas para entender meu algoritmo que tento criar. Basicamente, tenho duas listas e quero obter todas as combinações das duas listas.

Talvez eu não esteja explicando isso corretamente, então aqui está um exemplo.

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

a saída neste caso seria:

1.  A1 B2
2.  B1 A2

A parte complicada é que eu posso ter mais itens na variável "nome" do que itens na variável "número" (o número sempre será igual ou menor que a variável do nome).

Estou confuso sobre como fazer todas as combinações (aninhadas para loop?) E ainda mais confuso quanto à lógica para alterar os itens na variável name, caso haja mais itens no nome do que na lista de números.

Não sou o melhor programador, mas acho que posso tentar se alguém puder me ajudar a esclarecer a lógica / algoritmo para conseguir isso. Então, eu só fiquei preso em aninhados para loops.

Atualizar:

Aqui está a saída com 3 variáveis ​​e 2 números:

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

resultado:

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


1
@ dm03514 Vi isso e encontrei exemplos de objetivos um pouco semelhantes usando itertools, mas estou fazendo prototipagem em python, mas escreverei o código final em outro idioma, por isso não quero usar nenhuma ferramenta que não esteja disponível de outra maneira.
user1735075

1
O que você está pedindo realmente não faz sentido. Se a primeira lista contiver A, B, C e a segunda contiver 1,2, que resultado você esperaria? Isso pode ser feito se o exemplo que você deu tiver 4 resultados diferentes de uma letra e um número cada (A1, A2, B1, B2) ou se as duas listas tiverem o mesmo tamanho.
interjay

1
Eu concordo com o interjay. Especifique o resultado no caso de tamanho não igual, caso contrário, não é possível fornecer uma solução geral.
21412 Bakuriu

Oi pessoal, Atualizei a resposta para mostrar a saída com 3 nomes e 2 números .. Pensei que o explicasse bem, sem saber por que o voto negativo.
user1735075

Respostas:


93

Nota : Esta resposta é para a pergunta específica feita acima. Se você está aqui no Google e está procurando uma maneira de obter um produto cartesiano em Python, itertools.productou uma simples lista de compreensão pode ser o que você está procurando - veja as outras respostas.


Suponha len(list1) >= len(list2). Então o que você parecem querer é tomar todas as permutações de comprimento len(list2)de list1e combiná-los com itens de list2. Em python:

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

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

Devoluções

[[('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
O resultado é exatamente o que eu quero, mas é possível compartilhar a lógica por trás de como fazê-lo? Se eu converter meu código para C ou Java, eu não vou ter acesso a zip ou itertools (embora eles tornar a vida muito fácil)
user1735075

3
@ user1735075 Dê uma olhada na documentação
preguiça

1
@ user1735075: você sabe que o python é de código aberto? Então você pode simplesmente baixar as fontes e ver o que elas fazem. +1 ao Sr. Steak por apontar que a documentação realmente possui uma implementação de amostra que não usa zipe similar.
21412 Bakuriu

2
literalmente, não consigo fazer isso funcionar, mesmo com o seu exemplo ... tudo o que recebo é uma lista de objetos zip ..: |
M1nkeh

1
O @logic fornece qual deve ser a solução aceita.
Bernhard Wagner

502

A maneira mais simples é usar itertools.product:

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

11
O OP não estava solicitando um produto cartesiano, e essa resposta (assim como a maioria das outras) não fornece o resultado esperado especificado na pergunta.
interjay

17
@interjay, você está muito certo, mas como muitas pessoas parecem achar essa resposta correta, posso apenas assumir que o título da pergunta está sem contexto.
Xpy

3
@ xpy O título é muito curto para explicar tudo. É por isso que você precisa ler a pergunta real.
interjay

10
A OP queria permutações, mas o Google envia qualquer pessoa que esteja procurando combinações (como eu) a esta resposta - feliz em ver que ela tem 8 vezes os votos!
21418 Josh Friedlander

160

Pode ser mais simples que o mais simples acima:

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

sem qualquer importação


Melhor solução! Obrigado! Outras soluções estão errados simples ou única obra em casos específicos como a> b etc.
Philipp Schwarz

3
Solução mais Pythonic! (e evita importações desnecessárias)
Dalker

6
A complexidade do tempo é O (n ^ 2)
Deepak Sharma

2
Solução de apostas !! Bare básico é a melhor maneira sempre
Sabyasachi

22

Eu estava procurando por uma lista multiplicada por si só com apenas combinações únicas, fornecidas como essa função.

import itertools
itertools.combinations(list, n_times)

Aqui, como um trecho dos documentos do Python sobre itertools Isso, você pode encontrar o que procura.

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

Você pode tentar entender uma lista de uma linha:

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

a melhor maneira de descobrir todas as combinações para um grande número de listas é:

import itertools
from pprint import pprint

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

o resultado será:

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

Obrigado, ótima resposta!
toinbis 27/08/19

10

Ou a resposta do KISS para listas curtas:

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

Não é tão eficiente quanto as ferramentas, mas você está usando python, portanto o desempenho já não é sua principal preocupação ...

Eu também gosto de todas as outras respostas!


8

uma pequena melhoria para a resposta do interjay, para fazer o resultado como uma 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)]

referência a partir deste link


4

Sem ferramentas

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

4

Respondendo à pergunta "com duas listas, encontre todas as permutações possíveis de pares de um item de cada lista" e usando a funcionalidade básica do Python (ou seja, sem itertools) e, portanto, facilitando a replicação para outras linguagens de programação:

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)

Devoluções

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

2

As melhores respostas para isso funcionam apenas para comprimentos específicos de listas que são fornecidos.

Aqui está uma versão que funciona para qualquer comprimento de entrada. Também esclarece o algoritmo em termos dos conceitos matemáticos de combinação e permutação.

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

Isso gera:

0: A1 B2
1: A1 C2
2: B1 A2
3: B1 C2
4: C1 A2
5: C1 B2
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.