8 bits representando o número 7 são assim:
00000111
Três bits estão definidos.
O que são algoritmos para determinar o número de bits definidos em um número inteiro de 32 bits?
algorithm
binary
bit-manipulation
hammingweight
iec10967
Matt Howells
fonte
fonte
Respostas:
Isso é conhecido como ' Peso de Hamming ', 'popcount' ou 'adição lateral'.
O melhor algoritmo realmente depende de qual CPU você está e qual é o seu padrão de uso.
Algumas CPUs possuem uma única instrução interna para fazê-lo e outras possuem instruções paralelas que atuam em vetores de bits. As instruções paralelas (como x86
popcnt
, em CPUs onde é suportado) quase certamente serão mais rápidas. Algumas outras arquiteturas podem ter uma instrução lenta implementada com um loop microcodificado que testa um pouco por ciclo ( citação necessária ).Um método de pesquisa de tabela pré-preenchido pode ser muito rápido se sua CPU tiver um cache grande e / ou você estiver executando muitas dessas instruções em um loop restrito. No entanto, isso pode sofrer por causa da despesa de uma "falha de cache", em que a CPU precisa buscar parte da tabela da memória principal. (Procure cada byte separadamente para manter a tabela pequena.)
Se você souber que seus bytes serão geralmente 0 ou 1, então existem algoritmos muito eficientes para esses cenários.
Acredito que um algoritmo de uso geral muito bom seja o seguinte, conhecido como 'paralelo' ou 'algoritmo SWAR de precisão variável'. Eu expressei isso em uma pseudo linguagem C, você pode precisar ajustá-la para funcionar em uma linguagem específica (por exemplo, usando uint32_t para C ++ e >>> em Java):
Para JavaScript: coagir para inteiro com
|0
para desempenho: altere a primeira linha parai = (i|0) - ((i >> 1) & 0x55555555);
Esse tem o melhor comportamento de pior caso de qualquer um dos algoritmos discutidos; portanto, ele lida com eficiência com qualquer padrão de uso ou valores que você lançar nele.
Como esse bithack SWAR funciona:
O primeiro passo é uma versão otimizada do mascaramento para isolar os bits ímpares / pares, alternando para alinhá-los e adicionando. Isso efetivamente faz 16 adições separadas em acumuladores de 2 bits ( SWAR = SIMD dentro de um registro ). Like
(i & 0x55555555) + ((i>>1) & 0x55555555)
.A próxima etapa pega os ímpares / pares oito desses acumuladores de 16x e 2 bits e os adiciona novamente, produzindo somas 8x e 4 bits. A
i - ...
otimização não é possível neste momento para que ele não apenas mascarar antes / depois da mudança. Usar a mesma0x33...
constante nas duas vezes, em vez de0xccc...
antes da mudança, é uma boa coisa ao compilar ISAs que precisam construir constantes de 32 bits em registradores separadamente.A etapa final de troca e adição de
(i + (i >> 4)) & 0x0F0F0F0F
amplia para 4x acumuladores de 8 bits. Ele mascara após adicionar, em vez de antes, porque o valor máximo em qualquer acumulador de 4 bits é4
se todos os 4 bits dos bits de entrada correspondentes foram definidos. 4 + 4 = 8 que ainda cabe em 4 bits, portanto, é impossível transportar entre elementos de mordidelai + (i >> 4)
.Até agora, esse é apenas o SIMD normal, usando técnicas SWAR com algumas otimizações inteligentes. Continuar com o mesmo padrão por mais duas etapas pode aumentar para 2x 16 bits e 1x contagem de 32 bits. Mas existe uma maneira mais eficiente em máquinas com multiplicação rápida de hardware:
Uma vez que tenhamos poucos "elementos" suficientes, uma multiplicação por uma constante mágica pode somar todos os elementos no elemento superior . Nesse caso, elementos de byte. A multiplicação é feita deslocando-se para a esquerda e adicionando, portanto, uma multiplicação de
x * 0x01010101
resultados emx + (x<<8) + (x<<16) + (x<<24)
. Nossos elementos de 8 bits são amplos o suficiente (e mantêm contagens pequenas o suficiente) para que isso não produza efeito nos 8 bits superiores.Uma versão de 64 bits pode fazer elementos 8x de 8 bits em um número inteiro de 64 bits com um multiplicador 0x0101010101010101 e extrair o byte alto com
>>56
. Portanto, não são necessárias etapas extras, apenas constantes mais amplas. É isso que o GCC usa__builtin_popcountll
nos sistemas x86 quando apopcnt
instrução de hardware não está ativada. Se você pode usar componentes internos ou intrínsecos para isso, faça isso para que o compilador tenha a chance de fazer otimizações específicas de destino.Com SIMD completo para vetores mais amplos (por exemplo, contando uma matriz inteira)
Esse algoritmo SWAR bit a bit poderia ser paralelo para ser feito em vários elementos vetoriais de uma só vez, em vez de em um único registro inteiro, para acelerar as CPUs com SIMD, mas sem instrução utilizável de contagem de pop-ups. (por exemplo, código x86-64 que precisa ser executado em qualquer CPU, não apenas no Nehalem ou posterior.)
No entanto, a melhor maneira de usar instruções vetoriais para contagem pop-up é geralmente usando um shuffle variável para fazer uma pesquisa na tabela por 4 bits por vez de cada byte em paralelo. (Os 4 bits indexam uma tabela de 16 entradas mantida em um registro vetorial).
Nas CPUs Intel, a instrução popcnt de hardware de 64 bits pode superar uma implementação paralela de bits SSSE3
PSHUFB
em cerca de um fator de 2, mas apenas se o seu compilador acertar . Caso contrário, o SSE pode sair significativamente à frente. As versões mais recentes do compilador estão cientes do problema da dependência falsa popcnt na Intel .Referências:
fonte
unsigned int
, para mostrar facilmente que está livre de qualquer complicação. Também seriauint32_t
mais seguro, pois você consegue o que espera em todas as plataformas?>>
é definido pela implementação para valores negativos. O argumento precisa ser alterado (ou convertido) paraunsigned
e, como o código é específico de 32 bits, provavelmente deve estar sendo usadouint32_t
.Considere também as funções internas de seus compiladores.
No compilador GNU, por exemplo, você pode apenas usar:
Na pior das hipóteses, o compilador gerará uma chamada para uma função. Na melhor das hipóteses, o compilador emitirá uma instrução da CPU para fazer o mesmo trabalho mais rapidamente.
As intrínsecas do GCC até funcionam em várias plataformas. O Popcount se tornará popular na arquitetura x86; portanto, faz sentido começar a usar o intrínseco agora. Outras arquiteturas têm o número de habitantes há anos.
No x86, você pode dizer ao compilador que ele pode assumir suporte para
popcnt
instruções com-mpopcnt
ou-msse4.2
também habilitar as instruções vetoriais que foram adicionadas na mesma geração. Consulte as opções do GCC x86 .-march=nehalem
(ou-march=
qualquer CPU que você queira que seu código assuma e ajuste) pode ser uma boa escolha. A execução do binário resultante em uma CPU mais antiga resultará em uma falha de instrução ilegal.Para otimizar os binários para a máquina em que você os constrói, use
-march=native
(com gcc, clang ou ICC).O MSVC fornece um intrínseco para a
popcnt
instrução x86 , mas, ao contrário do gcc, é realmente intrínseco para a instrução de hardware e requer suporte de hardware.Usando em
std::bitset<>::count()
vez de um built-inEm teoria, qualquer compilador que saiba contabilizar eficientemente a CPU de destino deve expor essa funcionalidade por meio do ISO C ++
std::bitset<>
. Na prática, você pode estar melhor com o bit-hack AND / shift / ADD em alguns casos para algumas CPUs de destino.Para arquiteturas de destino em que o popcount de hardware é uma extensão opcional (como x86), nem todos os compiladores têm um
std::bitset
que aproveita quando disponível. Por exemplo, o MSVC não tem como habilitar opopcnt
suporte em tempo de compilação e sempre usa uma pesquisa de tabela , mesmo com/Ox /arch:AVX
(o que implica o SSE4.2, embora tecnicamente exista um bit de recurso separado parapopcnt
).Mas pelo menos você obtém algo portátil que funciona em qualquer lugar e, com o gcc / clang com as opções de destino corretas, você obtém o número de pop-ups de hardware para arquiteturas que o suportam.
Veja asm do gcc, clang, icc e MSVC no Godbolt compiler explorer.
x86-64
gcc -O3 -std=gnu++11 -mpopcnt
emite isso:Emissões do PowerPC64
gcc -O3 -std=gnu++11
(para aint
versão arg):Essa fonte não é específica para x86 ou GNU, mas apenas compila bem para x86 com gcc / clang / icc.
Observe também que o fallback do gcc para arquiteturas sem contagem de pop-up de instrução única é uma consulta de tabela de bytes por vez. Isso não é maravilhoso para o ARM, por exemplo .
fonte
std::bitset::count
. depois de inline, isso compila em uma única__builtin_popcount
chamada.Na minha opinião, a "melhor" solução é aquela que pode ser lida por outro programador (ou o programador original dois anos depois) sem grandes comentários. Você pode querer a solução mais rápida ou inteligente que alguns já forneceram, mas eu prefiro a legibilidade do que a inteligência a qualquer momento.
Se você quiser mais velocidade (e supondo que você a documente bem para ajudar seus sucessores), use uma pesquisa de tabela:
Embora eles dependam de tamanhos específicos de tipos de dados, não são tão portáteis. Porém, como muitas otimizações de desempenho não são portáveis, isso pode não ser um problema. Se você quer portabilidade, eu me ateria à solução legível.
fonte
if ((value & 1) == 1) { count++; }
comcount += value & 1
?Do prazer do hacker, p. 66, Figura 5-2
Executa em ~ 20-ish instruções (dependentes do arco), sem ramificação.
O prazer do hacker é delicioso! Altamente recomendado.
fonte
Integer.bitCount(int)
usa essa mesma implementação exata.pop
vez depopulation_count
(oupop_cnt
se você precisar de uma abreviação). @MarcoBolis Eu presumo que vai ser verdade para todas as versões do Java, mas oficialmente que seria dependente da implementação :)Acho que o caminho mais rápido - sem usar tabelas de pesquisa e contagem de pop - ups - é o seguinte. Conta os bits definidos com apenas 12 operações.
Isso funciona porque você pode contar o número total de bits definidos dividindo-os em duas metades, contando o número de bits definidos em ambas as metades e, em seguida, somando-os. Também conhecido como
Divide and Conquer
paradigma. Vamos entrar em detalhes ..O número de bits em dois bits pode ser
0b00
,0b01
ou0b10
. Vamos tentar resolver isso em 2 bits.Isso é necessário: a última coluna mostra a contagem de bits definidos em cada par de dois bits. Se o número dois bits é
>= 2 (0b10)
entãoand
produz0b01
, então ele produz0b00
.Essa afirmação deve ser fácil de entender. Após a primeira operação, temos a contagem de bits definidos a cada dois bits, agora somamos essa contagem a cada 4 bits.
Em seguida, somamos o resultado acima, fornecendo a contagem total de bits definidos em 4 bits. A última afirmação é a mais complicada.
Vamos dividir ainda mais ...
É semelhante à segunda declaração; estamos contando os bits definidos em grupos de 4. Sabemos - por causa de nossas operações anteriores - que toda mordidela tem a contagem de bits definidos. Vamos dar um exemplo. Suponha que tenhamos o byte
0b01000010
. Isso significa que o primeiro nibble tem seu conjunto de 4 bits e o segundo tem seu conjunto de 2 bits. Agora adicionamos esses petiscos.Ele nos fornece a contagem de bits definidos em um byte, na primeira mordida
0b01100010
e, portanto, mascaramos os últimos quatro bytes de todos os bytes do número (descartando-os).Agora, cada byte possui a contagem de bits definidos. Precisamos adicioná-los todos juntos. O truque é multiplicar o resultado pelo
0b10101010
qual possui uma propriedade interessante. Se nosso número tiver quatro bytes,A B C D
isso resultará em um novo número com esses bytesA+B+C+D B+C+D C+D D
. Um número de 4 bytes pode ter um conjunto máximo de 32 bits, que pode ser representado como0b00100000
.Tudo o que precisamos agora é o primeiro byte que tenha a soma de todos os bits definidos em todos os bytes, e nós o obtemos
>> 24
. Este algoritmo foi projetado para32 bit
palavras, mas pode ser facilmente modificado para64 bit
palavras.fonte
c =
? Parece que deve ser eliminado. Além disso, sugira um conjunto de parênteses extra A "(((v + (v >> 4)) e 0xF0F0F0F) * 0x1010101) >> 24" para evitar alguns avisos clássicos.popcount(int v)
epopcount(unsigned v)
. Para portabilidade, considerepopcount(uint32_t v)
, etc. Realmente gosto da parte * 0x1010101.return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
para não precisarmos contar letras para ver o que você está realmente fazendo (desde que você descartou a primeira0
, pensei acidentalmente que você usava o padrão de bits errado (invertido) como máscara - isto é, até eu notar que existem apenas 7 letras e não 8).Fiquei entediado e cronometrei um bilhão de iterações de três abordagens. O compilador é gcc -O3. CPU é o que eles colocam no Macbook Pro de 1ª geração.
O mais rápido é o seguinte, em 3,7 segundos:
O segundo lugar vai para o mesmo código, mas procurando 4 bytes em vez de 2 meias palavras. Isso levou cerca de 5,5 segundos.
O terceiro lugar está na abordagem de 'adição lateral', que levou 8,6 segundos.
O quarto lugar vai para __builtin_popcount (), em vergonhosos 11 segundos.
A abordagem de contar um bit de cada vez era muito mais lenta, e eu me cansei de esperar que ela terminasse.
Portanto, se você se preocupa com o desempenho acima de tudo, use a primeira abordagem. Se você se importa, mas não o suficiente para gastar 64 KB de RAM, use a segunda abordagem. Caso contrário, use a abordagem legível (mas lenta) de um bit de cada vez.
É difícil pensar em uma situação em que você queira usar a abordagem de manipulação de bits.
Edit: Resultados semelhantes aqui .
fonte
Se você estiver usando Java, o método
Integer.bitCount
interno fará isso.fonte
Deixe-me explicar esse algoritmo.
Este algoritmo é baseado no algoritmo de divisão e conquista. Suponha que haja um número inteiro de 8 bits 213 (11010101 em binário), o algoritmo funciona assim (cada vez que mescla dois blocos vizinhos):
fonte
Essa é uma daquelas perguntas em que ajuda a conhecer sua microarquitetura. Eu apenas cronometrei duas variantes no gcc 4.3.3 compiladas com -O3 usando inline C ++ para eliminar a sobrecarga de chamadas de função, um bilhão de iterações, mantendo a soma de todas as contagens para garantir que o compilador não remova nada de importante, usando rdtsc para cronometrar ( ciclo do relógio preciso).
O Hacker's Delight não modificado levou 12,2 gigaciclos. Minha versão paralela (contando o dobro de bits) é executada em 13,0 gigaciclos. Total de 10,5 segundos decorridos para ambos juntos em um Core Duo de 2,4 GHz. 25 gigaciclos = pouco mais de 10 segundos nessa freqüência de relógio, por isso estou confiante de que meus horários estão corretos.
Isso tem a ver com cadeias de dependência de instruções, que são muito ruins para esse algoritmo. Eu quase conseguia dobrar a velocidade novamente usando um par de registros de 64 bits. De fato, se eu fosse inteligente e adicionasse x + y um pouco antes, poderia cortar alguns turnos. A versão de 64 bits com alguns pequenos ajustes sairia equilibrada, mas contaria o dobro de bits novamente.
Com os registros SIMD de 128 bits, outro fator é dois, e os conjuntos de instruções SSE também possuem atalhos inteligentes.
Não há razão para o código ser especialmente transparente. A interface é simples, o algoritmo pode ser referenciado on-line em muitos lugares e é passível de testes de unidade abrangentes. O programador que se depara com isso pode até aprender alguma coisa. Essas operações de bits são extremamente naturais no nível da máquina.
OK, decidi testar a versão de 64 bits ajustada. Para esse tamanho único (sem assinatura) == 8
Parece certo (embora não esteja testando com cuidado). Agora, o tempo é de 10,70 gigacycles / 14,1 gigacycles. Esse número posterior somou 128 bilhões de bits e corresponde a 5,9s decorridos nesta máquina. A versão não paralela acelera um pouquinho, porque eu estou executando no modo de 64 bits e gosta de registros de 64 bits um pouco melhor que os de 32 bits.
Vamos ver se há um pouco mais de gasoduto OOO aqui. Isso foi um pouco mais envolvido, então eu realmente testei um pouco. Cada termo sozinho soma 64, todos somados a 256.
Fiquei empolgado por um momento, mas acontece que o gcc está fazendo truques inline com -O3, embora não esteja usando a palavra-chave inline em alguns testes. Quando deixei o gcc fazer truques, um bilhão de chamadas para pop4 () leva 12,56 gigaciclos, mas eu concluí que estava dobrando argumentos como expressões constantes. Um número mais realista parece ser 19,6gc para mais 30% de aceleração. Meu loop de teste agora se parece com isso, garantindo que cada argumento seja diferente o suficiente para impedir que o gcc faça truques.
256 bilhões de bits somados em 8,17s passaram. Funciona em 1,02s para 32 milhões de bits, como comparado na pesquisa de tabela de 16 bits. Não é possível comparar diretamente, porque o outro banco não fornece uma velocidade de clock, mas parece que eu dei um tapa na edição de tabela de 64 KB, que é um uso trágico do cache L1 em primeiro lugar.
Atualização: decidiu fazer o óbvio e criar pop6 () adicionando mais quatro linhas duplicadas. Chegando a 22,8 gc, 384 bilhões de bits somados em 9,5 segundos decorridos. Portanto, há mais 20% do Now a 800ms para 32 bilhões de bits.
fonte
Por que não iterativamente dividir por 2?
Concordo que este não é o mais rápido, mas o "melhor" é um tanto ambíguo. Eu argumentaria que o "melhor" deveria ter um elemento de clareza
fonte
A modificação de bits do Hacker's Delight se torna muito mais clara quando você escreve os padrões de bits.
O primeiro passo adiciona os bits pares aos ímpares, produzindo uma soma de bits em cada dois. As outras etapas adicionam pedaços de ordem superior a pedaços de ordem inferior, dobrando o tamanho do pedaço até o fim, até que a contagem final ocupe todo o int.
fonte
Para um meio termo entre uma tabela de pesquisa 2 32 e iterando cada bit individualmente:
Em http://ctips.pbwiki.com/CountBits
fonte
Isso pode ser feito em
O(k)
, ondek
está o número de bits definido.fonte
n &= (n-1)
.Não é a solução mais rápida ou melhor, mas encontrei a mesma pergunta no meu caminho e comecei a pensar e pensar. finalmente percebi que isso pode ser feito assim, se você pegar o problema do lado matemático e desenhar um gráfico, então descobrirá que é uma função que possui alguma parte periódica e, então, perceberá a diferença entre os períodos ... aqui está:
fonte
def f(i, d={0:lambda:0, 1:lambda:1, 2:lambda:1, 3:lambda:2}): return d.get(i, lambda: f(i//4) + f(i%4))()
A função que você está procurando costuma ser chamada de "soma lateral" ou "contagem da população" de um número binário. Knuth discute isso no pré-fascículo 1A, pp11-12 (embora tenha havido uma breve referência no volume 2, 4.6.3- (7).)
O locus classicus é o artigo de Peter Wegner "Uma técnica para contar em um computador binário", da Communications of the ACM , volume 3 (1960), número 5, página 322 . Ele fornece dois algoritmos diferentes, um otimizado para números que se espera "esparsos" (ou seja, possuem um número pequeno de um) e outro para o caso oposto.
fonte
fonte
Poucas perguntas em aberto: -
podemos modificar o algo para suportar o número negativo da seguinte maneira: -
Agora, para superar o segundo problema, podemos escrever o algo como:
para referência completa, consulte:
http://goursaha.freeoda.com/Misc Miscellaneous/IntegerBitCount.html
fonte
Acho que o método de Brian Kernighan também será útil ... Ele passa por tantas iterações quanto por bits definidos. Portanto, se tivermos uma palavra de 32 bits apenas com o conjunto de bits alto, ela passará apenas uma vez pelo loop.
fonte
Eu uso o código abaixo, que é mais intuitivo.
Lógica: n & (n-1) redefine o último bit definido de n.
PS: Eu sei que essa não é a solução O (1), embora seja uma solução interessante.
fonte
O(ONE-BITS)
. É de fato O (1), pois existem no máximo 32 bits.O que você quer dizer com "Melhor algoritmo"? O código em curto ou o código em jejum? Seu código parece muito elegante e possui um tempo de execução constante. O código também é muito curto.
Mas se a velocidade é o principal fator e não o tamanho do código, acho que o seguinte pode ser mais rápido:
Eu acho que isso não será mais rápido para um valor de 64 bits, mas um valor de 32 bits pode ser mais rápido.
fonte
Eu escrevi uma macro rápida de contagem de bits para máquinas RISC por volta de 1990. Ela não usa aritmética avançada (multiplicação, divisão,%), busca de memória (muito lenta), ramificações (muito lenta), mas assume que a CPU tem um Shifter de barril de 32 bits (em outras palavras, >> 1 e >> 32 levam a mesma quantidade de ciclos.) Pressupõe que pequenas constantes (como 6, 12, 24) não custam nada para carregar nos registradores ou são armazenadas temporários e reutilizados repetidamente.
Com essas suposições, conta 32 bits em cerca de 16 ciclos / instruções na maioria das máquinas RISC. Observe que 15 instruções / ciclos está próximo de um limite inferior no número de ciclos ou instruções, porque parece levar pelo menos 3 instruções (máscara, turno, operador) para reduzir pela metade o número de adendas, portanto log_2 (32) = 5, 5 x 3 = 15 instruções é quase um limite inferior.
Aqui está um segredo para o primeiro e mais complexo passo:
então, se eu pegar a 1ª coluna (A) acima, deslocar para a direita 1 bit e subtraí-la de AB, recebo a saída (CD). A extensão para 3 bits é semelhante; você pode verificá-lo com uma tabela booleana de 8 linhas como a minha acima, se desejar.
fonte
se você estiver usando C ++, outra opção é usar a metaprogramação de modelo:
o uso seria:
é claro que você poderia expandir ainda mais esse modelo para usar tipos diferentes (até mesmo o tamanho de bits de detecção automática), mas eu o mantive simples para maior clareza.
edit: esqueci de mencionar que isso é bom porque deve funcionar em qualquer compilador C ++ e basicamente desenrola seu loop para você se um valor constante for usado para a contagem de bits (em outras palavras, tenho certeza de que é o método geral mais rápido você encontrará)
fonte
constexpr
embora.Gosto particularmente deste exemplo do arquivo da sorte:
Eu gosto mais porque é tão bonito!
fonte
Java JDK1.5
Integer.bitCount (n);
onde n é o número cujos 1s devem ser contados.
verifique também
fonte
Eu encontrei uma implementação de contagem de bits em uma matriz com o uso da instrução SIMD (SSSE3 e AVX2). Tem desempenho 2-2,5 vezes melhor do que se usasse a função intrínseca __popcnt64.
Versão SSSE3:
Versão AVX2:
fonte
Eu sempre uso isso em Programação Competitiva e é fácil escrever e eficiente:
fonte
Existem muitos algoritmos para contar os bits definidos; mas acho que o melhor é o mais rápido! Você pode ver o detalhado nesta página:
Bit Twiddling Hacks
Eu sugiro este:
Contando bits definidos em palavras de 14, 24 ou 32 bits usando instruções de 64 bits
Este método requer que uma CPU de 64 bits com divisão de módulo rápida seja eficiente. A primeira opção leva apenas 3 operações; a segunda opção leva 10; e a terceira opção leva 15.
fonte
A solução C # rápida usando a tabela pré-calculada de bits de byte conta com ramificação no tamanho da entrada.
fonte
(0xe994 >>(k*2))&3
, sem acesso à memória ...Aqui está um módulo portátil (ANSI-C) que pode comparar cada um de seus algoritmos em qualquer arquitetura.
Sua CPU possui bytes de 9 bits? Não tem problema :-) No momento, ele implementa 2 algoritmos, o algoritmo K&R e uma tabela de consulta de bytes. A tabela de pesquisa é, em média, 3 vezes mais rápida que o algoritmo K&R. Se alguém conseguir descobrir uma maneira de tornar portátil o algoritmo "Hacker's Delight", sinta-se à vontade para adicioná-lo.
.
fonte
o que você pode fazer é
a lógica por trás disso é que os bits de n-1 são invertidos do bit mais à direita definido de n. se n = 6 ie 110, então 5 é 101, os bits são invertidos do bit mais à direita definido de n. Então, se nós e esses dois, criaremos o bit mais à direita 0 em cada iteração e sempre iremos para o próximo bit definido à direita. Portanto, contando o bit definido. A pior complexidade de tempo será O (logn) quando cada bit estiver definido.
fonte