The code below isn\'t working right for some inputs.
a, i = set(), 1
while i <= 10000:
a.add(i)
i <<=
Use *2 instead of bit shifts. Multiplication or addition are much more readable.
Refer to the excellent and detailed answer to "How to check if a number is a power of 2" — for C#. The equivalent Python implementation, also using the "bitwise and" operator &, is this:
def is_power_of_two(n):
return (n != 0) and (n & (n-1) == 0)
As Python has arbitrary-precision integers, this works for any integer n
as long as it fits into memory.
To summarize briefly the answer cited above: The first term, before the logical and
operator, simply checks if n
isn't 0 — and hence not a power of 2. The second term checks if it's a power of 2 by making sure that all bits after that bitwise &
operation are 0. The bitwise operation is designed to be only True
for powers of 2 — with one exception: if n
(and thus all of its bits) were 0 to begin with.
To add to this: As the logical and "short-circuits" the evaluation of the two terms, it would be more efficient to reverse their order if, in a particular use case, it is less likely that a given n
be 0 than it being a power of 2.
The best and most accurate approach would be to use bit manipulations:
(n & (n-1) == 0) and n != 0
Explanation: every power of 2 has exactly 1 bit set to 1 (the bit in that number's log base-2 index). So when subtracting 1 from it, that bit flips to 0 and all preceding bits flip to 1. That makes these 2 numbers the inverse of each other so when AND-ing them, we will get 0 as the result.
For example:
n = 8
decimal | 8 = 2**3 | 8 - 1 = 7 | 8 & 7 = 0
| ^ | |
binary | 1 0 0 0 | 0 1 1 1 | 1 0 0 0
| ^ | | & 0 1 1 1
index | 3 2 1 0 | | -------
0 0 0 0
-----------------------------------------------------
n = 5
decimal | 5 = 2**2 + 1 | 5 - 1 = 4 | 5 & 4 = 4
| | |
binary | 1 0 1 | 1 0 0 | 1 0 1
| | | & 1 0 0
index | 2 1 0 | | ------
1 0 0
So, in conclusion, whenever we subtract one from a number, AND the result with the number itself, and that becomes 0 - that number is a power of 2!
Of course, AND-ing anything with 0
will give 0, so we add the check for n != 0
.
You could always use some math functions, but those are less accurate:
import math
math.log(n, 2).is_integer()
Or:
math.log2(n).is_integer()
n <= 0
, both functions will throw a ValueError
as it is mathematically undefined (and therefore shouldn't present a logical problem).Or:
abs(math.frexp(n)[0]) == 0.5
Should be noted as well that for some numbers these functions are not accurate and actually give FALSE RESULTS:
math.log(2**29, 2).is_integer()
will give False
math.log2(2**49-1).is_integer()
will give True
math.frexp(2**53+1)[0] == 0.5
will give True
!!This is because math
functions use floats, and those have an inherent accuracy problem.
According to the math docs, the log
with a given base, actually calculates log(x)/log(base)
which is obviously slow. log2
is said to be more accurate, and probably more efficient. Bit manipulations are simple operations, not calling any functions.
So the results are:
log
withbase=2
: 0.67 sec
frexp
: 0.52 sec
log2
: 0.37 secbit ops: 0.2 sec
The code I used for these measures can be recreated in this REPL.
I have written a python function that will check the power of any number:
import math
def checkPowTwo(num):
x = int(input("The power of the number to be calculated is: "))
output = math.log(num, x)
residue = output - int(output)
if residue == 0:
print (num, " is a power of the number desired.")
else:
print (num, " is not a power of the number desired.")
y = checkPowTwo(int(input()))
The bin
builtin returns a string "0b1[01]?"
(regex notation) for every strictly positive integer (if system memory suffices, that is), so that we can write the Boolean expression
'1' not in bin(abs(n))[3:]
that yields True
for n
that equals 0
, 1
and 2**k
.
1
is 2**0
so it is unquestionably a power of two, but 0
is not, unless you take into account the limit of x=2**k
for k → -∞
. Under the second assumption we can write simply
check0 = lambda n: '1' not in bin(abs(n))[3:]
and under the first one (excluding 0
)
check1 = lambda n: '1' not in bin(abs(n))[3:] and n != 0
Of course the solution here proposed is just one of the many possible ones that
you can use to check if a number is a power of two... and for sure not the most
efficient one but I'm posting it in the sake of completeness :-)