Necesito una forma rápida de contar la cantidad de bits en un número entero en Python. Mi solución actual es
bin(n).count("1")
pero me pregunto si hay alguna forma más rápida de hacer esto.
PD: (estoy representando una gran matriz binaria 2D como una lista única de números y haciendo operaciones bit a bit, y eso reduce el tiempo de horas a minutos. Y ahora me gustaría deshacerme de esos minutos adicionales.
Editar: 1. tiene que estar en Python 2.7 o 2.6
y optimizar para números pequeños no importa mucho, ya que eso no sería un cuello de botella claro, pero tengo números con más de 10000 bits en algunos lugares
por ejemplo, este es un caso de 2000 bits:
12448057941136394342297748548545082997815840357634948550739612798732309975923280685245876950055614362283769710705811182976142803324242407017104841062064840113262840137625582646683068904149296501029754654149991842951570880471230098259905004533869130509989042199261339990315125973721454059973605358766253998615919997174542922163484086066438120268185904663422979603026066685824578356173882166747093246377302371176167843247359636030248569148734824287739046916641832890744168385253915508446422276378715722482359321205673933317512861336054835392844676749610712462818600179225635467147870208L
int
? ¿No tiene su propio método para calcular esto?int.bit_length
lo contrario, debería ser la respuesta, y no la aceptada a continuación.Respuestas:
Para enteros de longitud arbitraria,
bin(n).count("1")
es lo más rápido que pude encontrar en Python puro.Intenté adaptar las soluciones de Óscar y Adam para procesar el entero en trozos de 64 bits y 32 bits, respectivamente. Ambos fueron al menos diez veces más lentos que
bin(n).count("1")
(la versión de 32 bits tomó aproximadamente la mitad de tiempo).Por otro lado, gmpy
popcount()
tomó aproximadamente una vigésima parte del tiempo debin(n).count("1")
. Entonces, si puedes instalar gmpy, úsalo.Para responder una pregunta en los comentarios, para bytes usaría una tabla de búsqueda. Puede generarlo en tiempo de ejecución:
O simplemente defínalo literalmente:
Entonces es
counts[x]
para obtener el número de 1 bits enx
donde 0 ≤ x ≤ 255.fuente
bin(n).count("0")
no es exacto debido al prefijo '0b'. Tendría que serbin(n)[2:].count('0')
para aquellos que cuentan las travesuras ...numpy
matriz grande .bin(n).count("1")
. Sin embargo, solo supera el 60% de los envíos de Python. @ leetcodePuede adaptar el siguiente algoritmo:
Esto funciona para números positivos de 64 bits, pero es fácilmente ampliable y el número de operaciones crece con el logaritmo del argumento (es decir, linealmente con el tamaño de bits del argumento).
Para entender cómo funciona esto, imagine que divide toda la cadena de 64 bits en 64 depósitos de 1 bit. El valor de cada depósito es igual al número de bits establecidos en el depósito (0 si no se establecen bits y 1 si se establece un bit). La primera transformación da como resultado un estado análogo, pero con 32 cubos cada uno de 2 bits de longitud. Esto se logra cambiando apropiadamente los cubos y agregando sus valores (una adición se encarga de todos los cubos, ya que no puede ocurrir ningún acarreo entre cubos; el número de n bits siempre es lo suficientemente largo para codificar el número n). Las transformaciones adicionales conducen a estados con un número de cubos que disminuye exponencialmente y un tamaño que crece exponencialmente hasta que llegamos a un contenedor de 64 bits de longitud. Esto da el número de bits establecidos en el argumento original.
fuente
CountBits()
para manejar números de 10k bits agregando solo 8 líneas de código. Pero se volverá difícil de manejar debido a las enormes constantes.numpy
arreglos grandes .Aquí hay una implementación de Python del algoritmo de conteo de población, como se explica en esta publicación :
Funcionará para
0 <= i < 0x100000000
.fuente
bin(n).count("1")
.%timeit numberOfSetBits(23544235423)
:1000000 loops, best of 3: 818 ns per loop
;%timeit bitCountStr(23544235423)
:1000000 loops, best of 3: 577 ns per loop
.numberOfSetBits
procesa mis 864 × 64numpy.ndarray
en 841 µs. ConbitCountStr
tengo que hacer un bucle explícito, y tarda 40,7 ms, o casi 50 veces más.Según esta publicación , esta parece ser una de las implementaciones más rápidas del peso Hamming (si no le importa usar alrededor de 64 KB de memoria).
En Python 2.x debe reemplazarlo
range
conxrange
.Editar
Si necesita un mejor rendimiento (y sus números son enteros grandes), eche un vistazo a la
GMP
biblioteca. Contiene implementaciones de ensamblaje escritas a mano para muchas arquitecturas diferentes.gmpy
es un módulo de extensión de Python codificado en C que envuelve la biblioteca GMP.fuente
array.array
forPOPCOUNT_TABLE16
, ya que entonces se almacenará como una matriz de números enteros, en lugar de como una lista deint
objetos Python de tamaño dinámico .Me gusta mucho este método. Es simple y bastante rápido, pero tampoco está limitado en la longitud de bits, ya que Python tiene números enteros infinitos.
En realidad, es más astuto de lo que parece, porque evita perder el tiempo escaneando los ceros. Por ejemplo, tomará el mismo tiempo contar los bits establecidos en 1000000000000000000000010100000001 que en 1111.
fuente
bin(n).count("1")
pero tomó 3.8s para tu función. Si los números tuvieran muy pocos bits, funcionaría rápido, pero si toma cualquier número aleatorio, en promedio, la función anterior será órdenes de magnitud más lenta.Puede utilizar el algoritmo para obtener la cadena binaria [1] de un entero, pero en lugar de concatenar la cadena, contando el número de unos:
[1] https://wiki.python.org/moin/BitManipulation
fuente
Dijiste que Numpy era demasiado lento. ¿Lo estaba utilizando para almacenar bits individuales? ¿Por qué no extender la idea de usar ints como matrices de bits pero usar Numpy para almacenarlos?
Almacene n bits como una matriz de entradas de
ceil(n/32.)
32 bits. Luego puede trabajar con la matriz numpy de la misma manera (bueno, lo suficientemente similar) en la que usa ints, incluido su uso para indexar otra matriz.El algoritmo consiste básicamente en calcular, en paralelo, el número de bits establecidos en cada celda, y suman el recuento de bits de cada celda.
Aunque me sorprende que nadie te sugiera que escribas un módulo C.
fuente
fuente
Resulta que su representación inicial es una lista de listas de entradas que son 1 o 0. Simplemente cuéntelas en esa representación.
El número de bits en un entero es constante en Python.
Sin embargo, si desea contar el número de bits establecidos, la forma más rápida es crear una lista que se ajuste al siguiente pseudocódigo:
[numberofsetbits(n) for n in range(MAXINT)]
Esto le proporcionará una búsqueda de tiempo constante después de haber generado la lista. Vea la respuesta de @ PaoloMoretti para una buena implementación de esto. Por supuesto, no es necesario que guarde todo esto en la memoria; puede usar algún tipo de almacén de valores clave persistente o incluso MySql. (Otra opción sería implementar su propio almacenamiento simple basado en disco).
fuente