Hoy necesitaba un algoritmo simple para verificar si un número es una potencia de 2.
El algoritmo debe ser:
- Simple
- Correcto para cualquier
ulong
valor.
Se me ocurrió este algoritmo simple:
private bool IsPowerOfTwo(ulong number)
{
if (number == 0)
return false;
for (ulong power = 1; power > 0; power = power << 1)
{
// This for loop used shifting for powers of 2, meaning
// that the value will become 0 after the last shift
// (from binary 1000...0000 to 0000...0000) then, the 'for'
// loop will break out.
if (power == number)
return true;
if (power > number)
return false;
}
return false;
}
Pero luego pensé, ¿qué hay de verificar si es un número exactamente redondo? Pero cuando verifiqué 2 ^ 63 + 1, devolví exactamente 63 debido al redondeo. Así que verifiqué si 2 a la potencia 63 es igual al número original, y lo es, porque el cálculo se realiza en sy no en números exactos:log2 x
Math.Log
double
private bool IsPowerOfTwo_2(ulong number)
{
double log = Math.Log(number, 2);
double pow = Math.Pow(2, Math.Round(log));
return pow == number;
}
Esto devuelve true
el valor incorrecto dado: 9223372036854775809
.
¿Hay un mejor algoritmo?
(x & (x - 1))
puede devolver falsos positivos cuandoX
es una suma de potencias de dos, por ejemplo8 + 16
.Respuestas:
Hay un truco simple para este problema:
Tenga en cuenta, esta función será informar
true
de0
que no es una potencia de2
. Si desea excluir eso, así es como:Explicación
En primer lugar, el operador binario y bit a bit de la definición de MSDN:
Ahora echemos un vistazo a cómo se desarrolla todo esto:
La función devuelve boolean (verdadero / falso) y acepta un parámetro entrante de tipo unsigned long (x, en este caso). Por simplicidad, supongamos que alguien ha pasado el valor 4 y llamó a la función así:
Ahora reemplazamos cada aparición de x con 4:
Bueno, ya sabemos que 4! = 0 evals a verdadero, hasta ahora todo bien. Pero que pasa:
Esto se traduce en esto, por supuesto:
Pero que es exactamente
4&3
?La representación binaria de 4 es 100 y la representación binaria de 3 es 011 (recuerde que & toma la representación binaria de estos números). Entonces tenemos:
Imagine que estos valores se acumulan de manera muy similar a la suma elemental. El
&
operador dice que si ambos valores son iguales a 1, entonces el resultado es 1, de lo contrario es 0. Por lo tanto1 & 1 = 1
,1 & 0 = 0
,0 & 0 = 0
, y0 & 1 = 0
. Entonces hacemos los cálculos:El resultado es simplemente 0. Entonces volvemos y miramos lo que nuestra declaración de devolución ahora se traduce a:
Lo que se traduce ahora en:
Todos sabemos que
true && true
es simpletrue
, y esto muestra que, para nuestro ejemplo, 4 es una potencia de 2.fuente
Algunos sitios que documentan y explican este y otros trucos poco retorcidos son:
( http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 )
( http://bits.stephan-brumme.com/isPowerOfTwo.html )
Y el abuelo de ellos, el libro "Hacker's Delight" de Henry Warren, Jr .:
Como explica la página de Sean Anderson , la expresión
((x & (x - 1)) == 0)
indica incorrectamente que 0 es una potencia de 2. Sugiere usar:para corregir ese problema
fuente
!
que solo se puede aplicar a tipos booleanos, y&&
también requiere que ambos operandos sean booleanos- (Excepto que los operadores definidos por el usuario hacer otras cosas posibles, pero eso no es relevante paraulong
).return (i & -i) == i
fuente
i
cual se establece también se establecerá-i
. Los bits de abajo serán 0 (en ambos valores), mientras que los bits de arriba se invertirán uno con respecto al otro. Pori & -i
lo tanto, el valor de será el bit establecido menos significativoi
(que es una potencia de dos). Sii
tiene el mismo valor, ese fue el único conjunto de bits. Falla cuandoi
es 0 por la misma razón que loi & (i - 1) == 0
hace.i
es un tipo sin signo, dos complementos no tienen nada que ver con eso. Simplemente aprovecha las propiedades de la aritmética modular y bit a bit y.i==0
(devuelve lo(0&0==0)
que estrue
). Debería serreturn i && ( (i&-i)==i )
fuente
Recientemente escribí un artículo sobre esto en http://www.exploringbinary.com/ten-ways-to-check-if-an-integer-is-a-power-of-two-in-c/ . Cubre el conteo de bits, cómo usar los logaritmos correctamente, el clásico "x &&! (X & (x - 1))", y otros.
fuente
Aquí hay una solución simple de C ++ :
fuente
__builtin_popcount
. Desafortunadamente, una familia de procesadores aún no tiene una sola instrucción de ensamblaje para hacer esto (x86), por lo que es el método más rápido para contar bits. En cualquier otra arquitectura, esta es una sola instrucción de ensamblaje.popcnt
El siguiente anexo a la respuesta aceptada puede ser útil para algunas personas:
Una potencia de dos, cuando se expresa en binario, siempre se verá como 1 seguido de n ceros donde n es mayor o igual que 0. Ej:
y así.
Cuando restamos
1
de este tipo de números, se convierten en 0 seguidos de n unos y nuevamente n es igual que el anterior. Ex:y así.
Llegando al quid
El uno de
x
se alinea con el cero dex - 1
y todos los ceros dex
se alinean con los de unox - 1
, lo que hace que el bit Y resulte en 0. Y así es como tenemos la respuesta de una sola línea mencionada anteriormente correcta.Además de la belleza de la respuesta aceptada arriba:
Entonces, tenemos una propiedad a nuestra disposición ahora:
Un uso asombroso de esta propiedad es descubrir: ¿Cuántos 1 hay en la representación binaria de un número dado? El código corto y dulce para hacer eso para un número entero dado
x
es:Otro aspecto de los números que se puede probar a partir del concepto explicado anteriormente es "¿Se puede representar cada número positivo como la suma de las potencias de 2?".
Sí, cada número positivo se puede representar como la suma de las potencias de 2. Para cualquier número, tome su representación binaria. Ej: tomar el número
117
.fuente
Después de publicar la pregunta, pensé en la siguiente solución:
Necesitamos verificar si exactamente uno de los dígitos binarios es uno. Entonces, simplemente cambiamos el número a la derecha un dígito a la vez, y
true
regresamos si es igual a 1. Si en algún momento llegamos a un número impar ((number & 1) == 1
), sabemos que el resultado esfalse
. Esto demostró (usando un punto de referencia) un poco más rápido que el método original para valores verdaderos (grandes) y mucho más rápido para valores falsos o pequeños.Por supuesto, la solución de Greg es mucho mejor.
fuente
Y aquí hay un algoritmo general para descubrir si un número es una potencia de otro número.
fuente
fuente
c#
? Supongo que esto esc++
comox
se devuelve como un bool.Averigua si el número dado es una potencia de 2.
fuente
frexp
lugar delog
cosas desagradables si quieres usar coma flotante.fuente
Esto es realmente rápido Se necesitan aproximadamente 6 minutos y 43 segundos para verificar los 2 ^ 32 enteros.
fuente
Si
x
es una potencia de dos, su único bit 1 está en posiciónn
. Esto significa quex – 1
tiene un 0 en posiciónn
. Para ver por qué, recuerde cómo funciona una resta binaria. Al restar 1 dex
, el préstamo se propaga hasta la posiciónn
; el bit sen
convierte en 0 y todos los bits inferiores se convierten en 1. Ahora, dado quex
no tiene 1 bits en común conx – 1
,x & (x – 1)
es 0 y!(x & (x – 1))
es verdadero.fuente
Un número es una potencia de 2 si contiene solo 1 bit establecido. Podemos usar esta propiedad y la función genérica
countSetBits
para encontrar si un número tiene una potencia de 2 o no.Este es un programa C ++:
No necesitamos verificar explícitamente que 0 sea una Potencia de 2, ya que también devuelve False para 0.
SALIDA
fuente
while
lugar de unif
? Personalmente no puedo ver una razón, pero parece funcionar. EDITAR: - no ... ¿devolverá 1 por algo mayor que0
?Aquí hay otro método que ideé, en este caso usando en
|
lugar de&
:fuente
(x > 0)
bit aquí?para cualquier potencia de 2, lo siguiente también es válido.
n & (- n) == n
NOTA: falla para n = 0, por lo que debe verificarlo. La
razón por la que esto funciona es:
-n es el complemento 2s de n. -n tendrá cada bit a la izquierda del bit establecido más a la derecha de n invertido en comparación con n. Para potencias de 2 solo hay un bit establecido.
fuente
Ejemplo
Algoritmo
Usando una máscara de bits, divida
NUM
la variable en binarioIF R > 0 AND L > 0: Return FALSE
De lo contrario, se
NUM
convierte en el que no es ceroIF NUM = 1: Return TRUE
De lo contrario, vaya al Paso 1
Complejidad
Tiempo ~
O(log(d))
donded
es el número de dígitos binariosfuente
Mejorando la respuesta de @ user134548, sin bits aritméticos:
Esto funciona bien para:
fuente
Mark gravell sugirió esto si tiene .NET Core 3, System.Runtime.Intrinsics.X86.Popcnt.PopCount
Instrucción individual, más rápida que
(x != 0) && ((x & (x - 1)) == 0)
pero menos portátil.fuente
(x != 0) && ((x & (x - 1)) == 0)
? Lo dudo, especialmente. en sistemas más antiguos donde popcnt no está disponibleEn C, probé el
i && !(i & (i - 1)
truco y lo comparé con__builtin_popcount(i)
comparé con gcc en Linux, con el indicador -mpopcnt para asegurarme de usar la instrucción POPCNT de la CPU. Mi programa de prueba contó el número de enteros entre 0 y 2 ^ 31 que eran una potencia de dos.Al principio pensé que
i && !(i & (i - 1)
era un 10% más rápido, a pesar de que verifiqué que POPCNT se usaba en el desmontaje donde usaba__builtin_popcount
.Sin embargo, me di cuenta de que había incluido una declaración if, y la predicción de bifurcación probablemente estaba mejor en la versión bit twiddling. Eliminé el if y POPCNT terminó más rápido, como se esperaba.
Resultados:
Intel (R) Core (TM) i7-4771 CPU max 3.90GHz
Procesador AMD Ryzen Threadripper 2950X 16-Core max 3.50GHz
Tenga en cuenta que aquí la CPU Intel parece un poco más lenta que AMD con el bit twiddling, pero tiene un POPCNT mucho más rápido; AMD POPCNT no proporciona tanto impulso.
popcnt_test.c:
Ejecutar pruebas:
fuente
Veo que muchas respuestas sugieren devolver n &&! (N & (n - 1)) pero, según mi experiencia, si los valores de entrada son negativos, devuelve valores falsos. Compartiré otro enfoque simple aquí, ya que sabemos que una potencia de dos números tiene solo un bit establecido, por lo que simplemente contaremos el número de bits establecidos, esto llevará tiempo O (log N).
Consulte este artículo para contar no. de bits fijos
fuente
fuente
Este programa en Java devuelve "verdadero" si el número es una potencia de 2 y devuelve "falso" si no es una potencia de 2
fuente