Quero verificar se algum dos itens de uma lista está presente em outra lista. Posso fazer isso simplesmente com o código abaixo, mas suspeito que possa haver uma função de biblioteca para fazer isso. Caso contrário, existe um método mais pitônico de alcançar o mesmo resultado.
In [78]: a = [1, 2, 3, 4, 5]
In [79]: b = [8, 7, 6]
In [80]: c = [8, 7, 6, 5]
In [81]: def lists_overlap(a, b):
....: for i in a:
....: if i in b:
....: return True
....: return False
....:
In [82]: lists_overlap(a, b)
Out[82]: False
In [83]: lists_overlap(a, c)
Out[83]: True
In [84]: def lists_overlap2(a, b):
....: return len(set(a).intersection(set(b))) > 0
....:
list
python
intersection
fmark
fonte
fonte
len(...) > 0
porquebool(set([]))
produz False. E, é claro, se você mantivesse suas listas como conjuntos, você economizaria sobrecarga na criação de conjuntos.True
de1
eFalse
para0
.not set([1]).isdisjoint([True])
ficaTrue
, o mesmo com outras soluções.Respostas:
Resposta curta : use
not set(a).isdisjoint(b)
, geralmente é o mais rápido.Existem quatro maneiras comuns de testar se há duas listas
a
eb
compartilhar itens. A primeira opção é converter ambos em conjuntos e verificar sua interseção, como tal:Como os conjuntos são armazenados usando uma tabela de hash no Python, é possível pesquisá-los
O(1)
(consulte aqui para obter mais informações sobre a complexidade dos operadores no Python). Teoricamente, isso é,O(n+m)
em média, paran
em
objetos nas listasa
eb
. Mas 1) ele deve primeiro criar conjuntos das listas, o que pode levar um tempo não negligenciável, e 2) supõe que as colisões de hash sejam escassas entre seus dados.A segunda maneira de fazer isso é usar uma expressão de gerador executando iteração nas listas, como:
Isso permite pesquisar no local, para que nenhuma nova memória seja alocada para variáveis intermediárias. Também se destaca na primeira descoberta. Mas o
in
operador está sempreO(n)
nas listas (veja aqui ).Outra opção proposta é um híbrido para iterar através de uma das listas, converter a outra em um conjunto e testar a associação nesse conjunto, da seguinte forma:
Uma quarta abordagem é aproveitar o
isdisjoint()
método dos conjuntos (congelados) (veja aqui ), por exemplo:Se os elementos pesquisados estiverem perto do início de uma matriz (por exemplo, ela é classificada), a expressão do gerador é favorecida, pois o método de interseção de conjuntos precisa alocar nova memória para as variáveis intermediárias:
Aqui está um gráfico do tempo de execução para este exemplo em função do tamanho da lista:
Observe que os dois eixos são logarítmicos. Isso representa o melhor caso para a expressão do gerador. Como pode ser visto, o
isdisjoint()
método é melhor para tamanhos de lista muito pequenos, enquanto a expressão do gerador é melhor para tamanhos de lista maiores.Por outro lado, como a pesquisa começa com o início da expressão híbrida e geradora, se o elemento compartilhado estiver sistematicamente no final da matriz (ou as duas listas não compartilharem nenhum valor), as abordagens de interseção separada e definida serão então muito mais rápido que a expressão do gerador e a abordagem híbrida.
É interessante notar que a expressão do gerador é muito mais lenta para tamanhos de lista maiores. Isso é apenas para 1000 repetições, em vez de 100000 para a figura anterior. Essa configuração também se aproxima bem quando quando nenhum elemento é compartilhado e é o melhor caso para as abordagens de interseção separada e definida.
Aqui estão duas análises usando números aleatórios (em vez de manipular a configuração para favorecer uma técnica ou outra):
Grande chance de compartilhamento: os elementos são retirados aleatoriamente
[1, 2*len(a)]
. Baixa chance de compartilhamento: os elementos são retirados aleatoriamente[1, 1000*len(a)]
.Até agora, essa análise supunha que as duas listas eram do mesmo tamanho. No caso de duas listas de tamanhos diferentes, por exemplo,
a
é muito menor,isdisjoint()
é sempre mais rápido:Verifique se a
a
lista é menor, caso contrário, o desempenho diminui. Nesta experiência, oa
tamanho da lista foi definido como constante5
.Em suma:
not set(a).isdisjoint(b)
é sempre a mais rápida.any(i in a for i in b)
será a mais rápida em tamanhos de lista grandes;not set(a).isdisjoint(b)
, que é sempre mais rápida quebool(set(a) & set(b))
.a = set(a); any(i in a for i in b)
é geralmente mais lento que outros métodos.Na maioria dos casos, o uso do
isdisjoint()
método é a melhor abordagem, pois a expressão do gerador levará muito mais tempo para ser executada, pois é muito ineficiente quando nenhum elemento é compartilhado.fonte
any
sai no primeiro valor não falso. Usando uma lista em que o único valor correspondente está no final, obtemos o seguinte:timeit('any(i in a for i in b)', setup="a=list(range(1000));b=[x+998 for x in range(999,-0,-1)]", number=1000) 13.739536046981812
timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=[x+998 for x in range(999,-0,-1)]", number=1000) 0.08102107048034668
... e é apenas com 1000 iterações.not set(a).isdisjoint(b)
para testar se duas listas compartilham um membro.set(a).isdisjoint(b)
retornaTrue
se as duas listas não compartilham um membro. A resposta deve ser editada?Nota: o acima pressupõe que você deseja um booleano como resposta. Se tudo o que você precisa é de uma expressão para usar em uma
if
instrução, useif set(a) & set(b):
fonte
O(n + m)
. Meu palpite seria que os conjuntos são implementados usando tabelas de hash e, portanto, oin
operador pode trabalhar noO(1)
tempo (exceto em casos degenerados). Isso está correto? Em caso afirmativo, considerando que as tabelas de hash têm desempenho de pesquisa de pior casoO(n)
, isso significa que, no pior caso, ele teráO(n * m)
desempenho?O(n)
desempenho na pesquisa;), consulte pastebin.com/Kn3kAW7u Apenas para lafs.Isso é assintoticamente ideal (pior caso O (n + m)) e pode ser melhor que a abordagem de interseção devido ao
any
curto-circuito do circuito.Por exemplo:
retornará True assim que chegar
3 in sb
EDIT: Outra variação (com agradecimentos a Dave Kirby):
Isso depende
imap
do iterador, que é implementado em C, em vez de uma compreensão do gerador. Ele também usasb.__contains__
como a função de mapeamento. Não sei quanta diferença de desempenho isso faz. Ainda irá causar um curto-circuito.fonte
any(itertools.imap(sb.__contains__, a))
que deve ser mais rápido ainda, pois evita o uso de uma função lambda.Você também pode usar
any
com compreensão de lista:fonte
[]
) e ela seria executada mais rapidamente e consumiria menos memória, mas o tempo ainda seria O (n * m).No python 2.6 ou posterior, você pode fazer:
fonte
Você pode usar qualquer expressão interna da função / gerador de wa:
Como John e Lie apontaram, isso fornece resultados incorretos quando, para cada i compartilhado pelas duas listas, bool (i) == False. Deveria ser:
fonte
bool(x)
está False. No exemplo de Lie Ryan, x é 0. Somente a correção é aany(True for i in a if i in b)
que é melhor escrita como a já vistaany(i in b for i in a)
.x
na interseção são tais quebool(x)
éFalse
.Essa pergunta é bastante antiga, mas notei que, enquanto as pessoas discutiam conjuntos versus listas, ninguém pensava em usá-las juntas. Seguindo o exemplo de Soravux,
Pior caso para listas:
E o melhor caso para listas:
Portanto, ainda mais rápido do que a iteração nas duas listas é iterativo na lista para ver se ele está em um conjunto, o que faz sentido, pois verificar se um número está em um conjunto leva tempo constante, enquanto que a verificação pela iteração na lista leva um tempo proporcional ao tamanho de a lista.
Assim, minha conclusão é que iteramos através de uma lista e verificamos se está em um conjunto .
fonte
isdisjoint()
método em um conjunto (congelado), conforme indicado por @Toughy, é ainda melhor:timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
=> 0.00913715362548828se você não se importa com o que o elemento sobreposto pode ser, basta verificar a
len
lista combinada e a lista combinada como um conjunto. Se houver elementos sobrepostos, o conjunto será mais curto:len(set(a+b+c))==len(a+b+c)
retorna True, se não houver sobreposição.fonte
Vou lançar outro com um estilo de programação funcional:
Explicação:
retorna uma lista de booleanos em que elementos
b
são encontradosa
. Essa lista é então passada paraany
, que simplesmente retornaTrue
se houver algum elementoTrue
.fonte