Preciso de uma maneira rápida de contar o número de bits em um número inteiro em python. Minha solução atual é
bin(n).count("1")
mas estou me perguntando se existe alguma maneira mais rápida de fazer isso?
PS: (estou representando uma grande matriz binária 2D como uma única lista de números e fazendo operações bit a bit, o que reduz o tempo de horas para minutos. Agora, gostaria de me livrar desses minutos extras.
Editar: 1. tem que estar em python 2.7 ou 2.6
e otimizar para pequenos números não importa muito, pois isso não seria um gargalo claro, mas eu tenho números com mais de 10.000 bits em alguns lugares
por exemplo, este é um caso de 2.000 bits:
12448057941136394342297748548545082997815840357634948550739612798732309975923280685245876950055614362283769710705811182976142803324242407017104841062064840113262840137625582646683068904149296501029754654149991842951570880471230098259905004533869130509989042199261339990315125973721454059973605358766253998615919997174542922163484086066438120268185904663422979603026066685824578356173882166747093246377302371176167843247359636030248569148734824287739046916641832890744168385253915508446422276378715722482359321205673933317512861336054835392844676749610712462818600179225635467147870208L
int
? Isso não tem seu próprio método para calcular isso?int.bit_length
deve ser a resposta, e não a aceita abaixo.Respostas:
Para inteiros de comprimento arbitrário,
bin(n).count("1")
é o mais rápido que encontrei no Python puro.Tentei adaptar as soluções de Óscar e Adam para processar o inteiro em blocos de 64 bits e 32 bits, respectivamente. Ambos foram pelo menos dez vezes mais lentos do que
bin(n).count("1")
(a versão de 32 bits levou cerca de metade do tempo novamente).Por outro lado, o gmpy
popcount()
demorou cerca de 1/20 do tempo debin(n).count("1")
. Então, se você pode instalar o gmpy, use-o.Para responder a uma pergunta nos comentários, para bytes eu usaria uma tabela de pesquisa. Você pode gerá-lo em tempo de execução:
Ou apenas defina literalmente:
Então, é
counts[x]
para obter o número de 1 bits emx
que 0 ≤ x ≤ 255.fonte
bin(n).count("0")
não é preciso por causa do prefixo '0b'. Precisaria serbin(n)[2:].count('0')
para aqueles contando nada ....numpy
matriz.bin(n).count("1")
. No entanto, supera apenas 60% do envio de python. @ leetcodeVocê pode adaptar o seguinte algoritmo:
Isso funciona para números positivos de 64 bits, mas é facilmente extensível e o número de operações aumenta com o logaritmo do argumento (ou seja, linearmente com o tamanho de bits do argumento).
Para entender como isso funciona, imagine que você divida toda a string de 64 bits em 64 intervalos de 1 bit. O valor de cada depósito é igual ao número de bits definido no depósito (0 se nenhum bit for definido e 1 se um bit for definido). A primeira transformação resulta em um estado análogo, mas com 32 intervalos de 2 bits cada. Isso é conseguido deslocando apropriadamente os depósitos e adicionando seus valores (uma adição cuida de todos os depósitos, uma vez que nenhum transporte pode ocorrer entre os depósitos - o número de n bits é sempre longo o suficiente para codificar o número n). Outras transformações levam a estados com número exponencialmente decrescente de intervalos de tamanho crescente até chegarmos a um longo intervalo de 64 bits. Isso dá o número de bits definido no argumento original.
fonte
CountBits()
para lidar com números de 10k bits adicionando apenas 8 linhas de código. Mas se tornará difícil devido a constantes enormes.numpy
arrays.Esta é uma implementação em Python do algoritmo de contagem de população, conforme explicado nesta postagem :
Vai funcionar para
0 <= i < 0x100000000
.fonte
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
processa meu 864 × 64numpy.ndarray
em 841 µs. CombitCountStr
eu tenho que fazer um loop explicitamente, e leva 40,7 ms, ou quase 50 vezes mais.De acordo com esta postagem , esta parece ser uma das implementações mais rápidas do peso Hamming (se você não se importar em usar cerca de 64 KB de memória).
No Python 2.x você deve substituir
range
porxrange
.Editar
Se você precisa de melhor desempenho (e seus números são inteiros grandes), dê uma olhada na
GMP
biblioteca. Ele contém implementações de montagem escritas à mão para muitas arquiteturas diferentes.gmpy
é um módulo de extensão Python codificado em C que envolve a biblioteca GMP.fonte
array.array
paraPOPCOUNT_TABLE16
, já que será armazenado como um array de inteiros, em vez de uma lista deint
objetos Python de tamanho dinâmico .Eu realmente gosto desse método. É simples e muito rápido, mas também não é limitado no comprimento de bits, pois o python tem números inteiros infinitos.
Na verdade, é mais astuto do que parece, porque evita perder tempo digitalizando os zeros. Por exemplo, levará o mesmo tempo para contar os bits definidos em 1000000000000000000000010100000001 como em 1111.
fonte
bin(n).count("1")
mas demorou 3,8s para sua função. Se os números tivessem poucos bits definidos, funcionaria rápido, mas se você pegar qualquer número aleatório, em média, a função acima será ordens de magnitude mais lenta.Você pode usar o algoritmo para obter a string binária [1] de um inteiro, mas em vez de concatenar a string, contando o número de unidades:
[1] https://wiki.python.org/moin/BitManipulation
fonte
Você disse que Numpy era muito lento. Você o estava usando para armazenar bits individuais? Por que não estender a ideia de usar ints como arrays de bits, mas usar o Numpy para armazená-los?
Armazene n bits como uma matriz de
ceil(n/32.)
ints de 32 bits. Você pode então trabalhar com o array numpy da mesma maneira (bem, semelhante o suficiente) que você usa ints, incluindo usá-los para indexar outro array.O algoritmo consiste basicamente em calcular, em paralelo, o número de bits definido em cada célula, e eles somam a contagem de bits de cada célula.
Embora esteja surpreso que ninguém tenha sugerido que você escrevesse um módulo C.
fonte
fonte
Acontece que sua representação inicial é uma lista de listas de ints que são 1 ou 0. Basta contá-los nessa representação.
O número de bits em um inteiro é constante em python.
No entanto, se você quiser contar o número de bits definidos, a maneira mais rápida é criar uma lista em conformidade com o seguinte pseudocódigo:
[numberofsetbits(n) for n in range(MAXINT)]
Isso fornecerá uma pesquisa de tempo constante depois de gerar a lista. Veja a resposta de @PaoloMoretti para uma boa implementação disso. Claro, você não precisa manter tudo isso na memória - você pode usar algum tipo de armazenamento de valor-chave persistente, ou até mesmo MySql. (Outra opção seria implementar seu próprio armazenamento simples baseado em disco).
fonte