The code below isn't working right for some inputs.
a, i = set(), 1
while i <= 10000:
a.add(i)
i <<= 1
N = int(input())
if N in a:
print("True")
else:
print("False")
My initial idea was to check for each input if it's a power of 2 by starting from 1 and multiplying by 2 until exceeding the input number, comparing at each step. Instead, I store all the powers of 2 in a set
beforehand, in order to check a given input in O(1)
. How can this be improved?
Best Answer
Bit Manipulations
One approach would be to use bit manipulations:
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:
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 forn != 0
.math
functionsYou could always use math functions, but notice that using them without care could cause incorrect results:
math.log(x[, base])
withbase=2
:math.log2(x)
:Worth noting that for any
n <= 0
, both functions will throw aValueError
as it is mathematically undefined (and therefore shouldn't present a logical problem).math.frexp(x)
:As noted above, for some numbers these functions are not accurate and actually give FALSE RESULTS:
math.log(2**29, 2).is_integer()
will giveFalse
math.log2(2**49-1).is_integer()
will giveTrue
math.frexp(2**53+1)[0] == 0.5
will giveTrue
!!This is because
math
functions use floats, and those have an inherent accuracy problem.(Expanded) Timing
Some time has passed since this question was asked and some new answers came up with the years. I decided to expand the timing to include all of them.
According to the math docs, the
log
with a given base, actually calculateslog(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:
The code I used for these measures can be recreated in this REPL (forked from this one).