How to check if a given number is a power of two?

前端 未结 5 823
说谎
说谎 2020-12-01 16:23

The code below isn\'t working right for some inputs.

a, i = set(), 1
while i <= 10000:
    a.add(i)
    i <<=         


        
相关标签:
5条回答
  • 2020-12-01 16:33

    Use *2 instead of bit shifts. Multiplication or addition are much more readable.

    0 讨论(0)
  • 2020-12-01 16:35

    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.

    0 讨论(0)
  • 2020-12-01 16:38

    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()
    
    • Worth noting that for any 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.


    Timing

    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 with base=2: 0.67 sec

    frexp: 0.52 sec

    log2: 0.37 sec

    bit ops: 0.2 sec

    The code I used for these measures can be recreated in this REPL.

    0 讨论(0)
  • 2020-12-01 16:38

    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()))
    
    0 讨论(0)
  • 2020-12-01 16:40

    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 :-)

    0 讨论(0)
提交回复
热议问题