Existe uma função de biblioteca que realiza pesquisa binária em uma lista / tupla e retorna a posição do item, se encontrado, e 'Falso' (-1, Nenhum, etc.), se não?
Encontrei as funções bisect_left / right no módulo bisect , mas elas ainda retornam uma posição mesmo que o item não esteja na lista. Isso é perfeitamente adequado para o uso pretendido, mas eu só quero saber se um item está na lista ou não (não quero inserir nada).
Pensei em usar bisect_left
e depois verificar se o item nessa posição é igual ao que estou procurando, mas isso parece complicado (e também preciso verificar os limites se o número pode ser maior que o maior número da minha lista). Se houver um método melhor, eu gostaria de saber sobre isso.
Editar Para esclarecer o que eu preciso disso: estou ciente de que um dicionário seria muito adequado para isso, mas estou tentando manter o consumo de memória o mais baixo possível. Meu uso pretendido seria uma espécie de tabela de consulta de duas vias. Eu tenho na tabela uma lista de valores e preciso poder acessar os valores com base em seu índice. E também quero encontrar o índice de um valor específico ou Nenhum se o valor não estiver na lista.
Usar um dicionário para isso seria a maneira mais rápida, mas dobraria (aproximadamente) os requisitos de memória.
Eu estava fazendo essa pergunta pensando que poderia ter esquecido algo nas bibliotecas Python. Parece que vou ter que escrever meu próprio código, como sugeriu Moe.
fonte
np.searchsorted
é útil. docs.scipy.org/doc/numpy/reference/generated/…Respostas:
fonte
if hi is None: hi = len(a)
Por que não olhar para o código bisect_left / right e adaptá-lo para se adequar ao seu objetivo.
como isso:
fonte
hi = mid - 1
noelif
?hi = mid
parahi = mid-1
ehi = len(a)
dehi = len(a)-1
ewhile lo < hi:
parawhile lo <= hi
, e seria equivalente corretabisect.bisect_left()
em vez disso.Isso é um pouco fora de tópico (já que a resposta de Moe parece completa para a pergunta do OP), mas pode valer a pena examinar a complexidade de todo o procedimento de ponta a ponta. Se você estiver armazenando algo em listas classificadas (que é onde uma pesquisa binária ajudaria) e depois apenas verificando a existência, está ocorrendo (na pior das hipóteses, a menos que seja especificado):
Listas ordenadas
Considerando que com a
set()
, você está incorrendoA única coisa que uma lista classificada realmente mostra é "próximo", "anterior" e "intervalos" (incluindo a inserção ou exclusão de intervalos), que são O (1) ou O (| intervalo |), com um índice inicial. Se você não estiver usando esse tipo de operação com frequência, armazenar como conjuntos e classificar para exibição pode ser um negócio melhor em geral.
set()
incorre muito pouco em sobrecarga adicional em python.fonte
Vale a pena mencionar que os documentos bisectados agora fornecem exemplos de pesquisa: http://docs.python.org/library/bisect.html#searching-sorted-lists
(Aumentar ValueError em vez de retornar -1 ou None é mais pythonic - list.index () faz, por exemplo. Mas é claro que você pode adaptar os exemplos às suas necessidades.)
fonte
O mais simples é usar o bisset e verificar uma posição novamente para ver se o item está lá:
fonte
Isso está correto no manual:
http://docs.python.org/2/library/bisect.html
8.5.1 Pesquisando Listas Classificadas
As funções bisect () acima são úteis para encontrar pontos de inserção, mas podem ser difíceis ou difíceis de usar para tarefas comuns de pesquisa. As cinco funções a seguir mostram como transformá-las nas pesquisas padrão para listas classificadas:
Portanto, com a pequena modificação, seu código deve ser:
fonte
Concordo que a resposta de @ DaveAbrahams usando o módulo bisect é a abordagem correta. Ele não mencionou um detalhe importante em sua resposta.
Dos documentos
bisect.bisect_left(a, x, lo=0, hi=len(a))
O módulo de bissecção não exige que a matriz de pesquisa seja pré-computada com antecedência. Você pode apenas apresentar os pontos de extremidade ao
bisect.bisect_left
invés dele, usando os padrões de0
elen(a)
.Ainda mais importante para o meu uso, procurando um valor X tal que o erro de uma determinada função seja minimizado. Para fazer isso, eu precisava de uma maneira de o algoritmo do bisect_left chamar minha computação. Isto é realmente simples.
Basta fornecer um objeto que define
__getitem__
comoa
Por exemplo, poderíamos usar o algoritmo bisect para encontrar uma raiz quadrada com precisão arbitrária!
fonte
scipy.optimize
para isso.Se você quiser apenas ver se está presente, tente transformar a lista em um ditado:
Na minha máquina, "se n em l" levou 37 segundos, enquanto "se n em d" levou 0,4 segundos.
fonte
Esta está:
fonte
A solução de Dave Abrahams é boa. Embora eu tivesse feito isso minimalista:
fonte
Embora não exista um algoritmo explícito de pesquisa binária no Python, existe um módulo -
bisect
- projetado para encontrar o ponto de inserção de um elemento em uma lista classificada usando uma pesquisa binária. Isso pode ser "enganado" na realização de uma pesquisa binária. A maior vantagem disso é a mesma vantagem que o código de biblioteca tem - é de alto desempenho, bem testado e simplesmente funciona (pesquisas binárias em particular podem ser bastante difíceis de implementar com sucesso - principalmente se os casos extremos não forem considerados com cuidado).Tipos básicos
Para tipos básicos como Strings ou ints, é muito fácil - tudo o que você precisa é do
bisect
módulo e de uma lista classificada:Você também pode usar isso para encontrar duplicatas:
Obviamente, você pode simplesmente retornar o índice em vez do valor nesse índice, se desejar.
Objetos
Para tipos ou objetos personalizados, as coisas são um pouco mais complicadas: você precisa implementar métodos de comparação sofisticados para que a bissecção seja comparada corretamente.
Isso deve funcionar em pelo menos Python 2.7 -> 3.3
fonte
Usar um ditado não gostaria de duplicar o uso de memória, a menos que os objetos que você esteja armazenando sejam realmente minúsculos, pois os valores são apenas indicadores dos objetos reais:
Nesse exemplo, 'foo' é armazenado apenas uma vez. Isso faz diferença para você? E exatamente de quantos itens estamos falando?
fonte
Esse código funciona com listas inteiras de maneira recursiva. Procura o cenário de caso mais simples, que é: comprimento da lista menor que 2. Isso significa que a resposta já está lá e um teste é executado para verificar a resposta correta. Caso contrário, um valor do meio é definido e testado para ser o correto, caso contrário a bissecção é executada chamando novamente a função, mas definindo o valor do meio como o limite superior ou inferior, deslocando-o para a esquerda ou direita
fonte
Confira os exemplos na Wikipedia http://en.wikipedia.org/wiki/Binary_search_algorithm
fonte
Eu acho que isso é muito melhor e eficaz. Por favor me corrija :) . Obrigado
fonte
s
é uma lista.binary(s, 0, len(s) - 1, find)
é a chamada inicial.A função retorna um índice do item consultado. Se não houver esse item, ele retornará
-1
.fonte
fonte
Pesquisa binária:
// Para chamar a função acima, use:
fonte
Eu precisava de pesquisa binária em python e genérica para modelos do Django. Nos modelos do Django, um modelo pode ter chave estrangeira para outro modelo e eu queria realizar alguma pesquisa nos objetos de modelos recuperados. Eu escrevi a seguinte função, você pode usar isso.
fonte
Muitas boas soluções acima, mas eu não vi um uso estúpido (o KISS simplifica (porque eu sou)) da função bisect incorporada / genérica do Python para fazer uma pesquisa binária.Com um pouco de código em torno da função bisect, Eu acho que tenho um exemplo abaixo, onde testei todos os casos para uma pequena matriz de nomes de caracteres.Algumas das soluções acima mencionam / dizem isso, mas espero que o código simples abaixo ajude qualquer pessoa confusa como eu.
O Python bisect é usado para indicar onde inserir um novo valor / item de pesquisa em uma lista classificada. O código abaixo que usa bisect_left que retornará o índice da ocorrência se o item de pesquisa na lista / matriz for encontrado (observe bisect e bisect_right retornará o índice do elemento após a ocorrência ou correspondência como ponto de inserção) Se não encontrado , bisect_left retornará um índice para o próximo item na lista classificada que não será == o valor da pesquisa. O único outro caso é onde o item de pesquisa iria no final da lista, onde o índice retornado estaria além do final da lista / matriz e que, no código abaixo da saída antecipada do Python, com identificadores lógicos "e". (primeira condição, o False Python não verifica as condições subseqüentes)
fonte