¿Existe una función de biblioteca que realiza una búsqueda binaria en una lista / tupla y devuelve la posición del elemento si se encuentra y 'Falso' (-1, Ninguno, etc.) si no?
Encontré las funciones bisect_left / right en el módulo bisect , pero aún así devuelven una posición incluso si el elemento no está en la lista. Eso está perfectamente bien para su uso previsto, pero solo quiero saber si un elemento está en la lista o no (no quiero insertar nada).
Pensé en usar bisect_left
y luego verificar si el elemento en esa posición es igual a lo que estoy buscando, pero eso parece engorroso (y también necesito hacer límites para verificar si el número puede ser mayor que el número más grande en mi lista). Si hay un método mejor, me gustaría saberlo.
Editar Para aclarar para qué necesito esto: soy consciente de que un diccionario sería muy adecuado para esto, pero estoy tratando de mantener el consumo de memoria lo más bajo posible. Mi uso previsto sería una especie de tabla de búsqueda de doble sentido. Tengo en la tabla una lista de valores y necesito poder acceder a los valores en función de su índice. Y también quiero poder encontrar el índice de un valor particular o Ninguno si el valor no está en la lista.
Usar un diccionario para esto sería la forma más rápida, pero (aproximadamente) duplicaría los requisitos de memoria.
Estaba haciendo esta pregunta pensando que podría haber pasado por alto algo en las bibliotecas de Python. Parece que tendré que escribir mi propio código, como sugirió Moe.
fuente
np.searchsorted
es útil. docs.scipy.org/doc/numpy/reference/generated/…Respuestas:
fuente
if hi is None: hi = len(a)
¿Por qué no mirar el código para bisect_left / right y adaptarlo a su propósito?
Me gusta esto:
fuente
hi = mid - 1
en elelif
?hi = mid
ahi = mid-1
yhi = len(a)
ahi = len(a)-1
ywhile lo < hi:
awhile lo <= hi
, y sería lo que es equivalente correctabisect.bisect_left()
lugar de esto.Esto es un poco fuera de tema (ya que la respuesta de Moe parece completa a la pregunta del OP), pero podría valer la pena mirar la complejidad de todo el procedimiento de principio a fin. Si está almacenando algo en una lista ordenada (que es donde una búsqueda binaria ayudaría), y luego solo verificando la existencia, está incurriendo (en el peor de los casos, a menos que se especifique):
Listas ordenadas
Mientras que con un
set()
, estás incurriendoLo que realmente obtiene una lista ordenada es "siguiente", "anterior" y "rangos" (incluida la inserción o eliminación de rangos), que son O (1) u O (| rango |), dado un índice inicial. Si no está utilizando ese tipo de operaciones con frecuencia, almacenar en conjunto y ordenar para mostrar podría ser una mejor opción en general.
set()
incurre muy poca sobrecarga adicional en python.fuente
Vale la pena mencionar que los documentos bisectos ahora proporcionan ejemplos de búsqueda: http://docs.python.org/library/bisect.html#searching-sorted-lists
(Elevar ValueError en lugar de devolver -1 o None es más pitónico: list.index () lo hace, por ejemplo. Pero, por supuesto, puede adaptar los ejemplos a sus necesidades).
fuente
Lo más simple es usar bisect y verificar una posición hacia atrás para ver si el artículo está allí:
fuente
Esto es correcto del manual:
http://docs.python.org/2/library/bisect.html
8.5.1. Buscar listas ordenadas
Las funciones bisect () anteriores son útiles para encontrar puntos de inserción pero pueden ser difíciles o difíciles de usar para tareas de búsqueda comunes. Las siguientes cinco funciones muestran cómo transformarlas en las búsquedas estándar para listas ordenadas:
Entonces, con la ligera modificación, su código debería ser:
fuente
Estoy de acuerdo en que la respuesta de @ DaveAbrahams utilizando el módulo bisect es el enfoque correcto. No mencionó un detalle importante en su respuesta.
De los documentos
bisect.bisect_left(a, x, lo=0, hi=len(a))
El módulo de bisección no requiere que la matriz de búsqueda se calcule previamente con anticipación. Simplemente puede presentar los puntos finales en
bisect.bisect_left
lugar de usar los valores predeterminados de0
ylen(a)
.Aún más importante para mi uso, buscar un valor X tal que se minimice el error de una función determinada. Para hacer eso, necesitaba una forma de hacer que el algoritmo de bisect_left llamara a mi cálculo. Esto es realmente simple.
Solo proporcione un objeto que defina
__getitem__
comoa
¡Por ejemplo, podríamos usar el algoritmo de bisección para encontrar una raíz cuadrada con precisión arbitraria!
fuente
scipy.optimize
para esto.Si solo quiere ver si está presente, intente convertir la lista en un dict:
En mi máquina, "if n in l" tomó 37 segundos, mientras que "if n in d" tomó 0.4 segundos.
fuente
Este es:
fuente
La solución de Dave Abrahams es buena. Aunque lo habría hecho minimalista:
fuente
Si bien no hay un algoritmo de búsqueda binaria explícito en Python, hay un módulo
bisect
diseñado para encontrar el punto de inserción de un elemento en una lista ordenada mediante una búsqueda binaria. Esto puede ser "engañado" para que realice una búsqueda binaria. La mayor ventaja de esto es la misma ventaja que tiene la mayoría del código de la biblioteca: es de alto rendimiento, está bien probado y simplemente funciona (las búsquedas binarias en particular pueden ser bastante difíciles de implementar con éxito , especialmente si los casos límite no se consideran cuidadosamente).Tipos basicos
Para tipos básicos como cadenas o entradas es bastante fácil: todo lo que necesita es el
bisect
módulo y una lista ordenada:También puede usar esto para encontrar duplicados:
Obviamente, si lo desea, puede devolver el índice en lugar del valor de ese índice.
Objetos
Para los tipos u objetos personalizados, las cosas son un poco más complicadas: debe asegurarse de implementar métodos de comparación enriquecidos para obtener una bisección para comparar correctamente.
Esto debería funcionar al menos en Python 2.7 -> 3.3
fuente
Al usar un dict no le gustaría duplicar el uso de su memoria a menos que los objetos que está almacenando sean realmente pequeños, ya que los valores son solo punteros a los objetos reales:
En ese ejemplo, 'foo' solo se almacena una vez. ¿Eso hace la diferencia para ti? ¿Y exactamente de cuántos artículos estamos hablando?
fuente
Este código funciona con listas enteras de forma recursiva. Busca el escenario de caso más simple, que es: longitud de la lista menor que 2. Significa que la respuesta ya está allí y se realiza una prueba para verificar la respuesta correcta. De lo contrario, se establece un valor medio y se prueba que sea el correcto, si no se realiza la bisección llamando nuevamente a la función, pero estableciendo el valor medio como el límite superior o inferior, desplazándolo hacia la izquierda o la derecha
fuente
Consulte los ejemplos en Wikipedia http://en.wikipedia.org/wiki/Binary_search_algorithm
fuente
Supongo que esto es mucho mejor y efectivo. por favor corrigeme :) . Gracias
fuente
s
es una listabinary(s, 0, len(s) - 1, find)
es la llamada inicialLa función devuelve un índice del elemento consultado. Si no hay tal artículo, regresa
-1
.fuente
fuente
Búsqueda binaria :
// Para llamar a la función anterior use:
fuente
Necesitaba búsqueda binaria en python y genérica para los modelos Django. En los modelos Django, un modelo puede tener una clave foránea para otro modelo y quería realizar alguna búsqueda en los objetos de los modelos recuperados. Escribí la siguiente función, puedes usar esto.
fuente
Muchas buenas soluciones anteriores, pero no he visto un uso simple (KISS lo mantiene simple (porque soy) estúpido de la función bisecta / genérica incorporada de Python para hacer una búsqueda binaria. Con un poco de código alrededor de la función bisecta, Creo que tengo un ejemplo a continuación donde probé todos los casos para una pequeña serie de nombres de cadenas. Algunas de las soluciones anteriores aluden / dicen esto, pero espero que el código simple a continuación ayude a cualquiera a confundirse como yo.
Python bisect se usa para indicar dónde insertar un nuevo valor / elemento de búsqueda en una lista ordenada. El siguiente código que usa bisect_left que devolverá el índice del hit si se encuentra el elemento de búsqueda en la lista / matriz (tenga en cuenta que bisect y bisect_right devolverán el índice del elemento después del hit o match como punto de inserción) Si no se encuentra , bisect_left devolverá un índice al siguiente elemento de la lista ordenada que no == el valor de búsqueda. El único otro caso es donde el elemento de búsqueda iría al final de la lista donde el índice devuelto estaría más allá del final de la lista / matriz, y que en el código debajo de la salida temprana de Python con "y" maneja la lógica. (primera condición False Python no verifica condiciones posteriores)
fuente