How to check if an integer is a power of 3?

前端 未结 23 1091
没有蜡笔的小新
没有蜡笔的小新 2020-12-02 08:15

I saw this question, and pop up this idea.

相关标签:
23条回答
  • 2020-12-02 08:19

    This is a summary of all good answers below this questions, and the performance figures can be found from the LeetCode article.

    1. Loop Iteration

    Time complexity O(log(n)), space complexity O(1)

    public boolean isPowerOfThree(int n) {
        if (n < 1) {
            return false;
        }
    
        while (n % 3 == 0) {
            n /= 3;
        }
    
        return n == 1;
    }
    

    2. Base Conversion

    Convert the integer to a base 3 number, and check if it is written as a leading 1 followed by all 0. It is inspired by the solution to check if a number is power of 2 by doing n & (n - 1) == 0

    Time complexity: O(log(n)) depending on language and compiler, space complexity: O(log(n))

    public boolean isPowerOfThree(int n) {
        return Integer.toString(n, 3).matches("^10*$");
    }
    

    3 Mathematics

    If n = 3^i, then i = log(n) / log(3), and thus comes to the solution

    Time complexity: depending on language and compiler, space complexity: O(1)

    public boolean isPowerOfThree(int n) {
        return (Math.log(n) / Math.log(3) + epsilon) % 1 <= 2 * epsilon;
    }
    

    4 Integer Limitations

    Because 3^19 = 1162261467 is the largest power of 3 number fits in a 32 bit integer, thus we can do

    Time complexity: O(1), space complexity: O(1)

    public boolean isPowerOfThree(int n) {
        return n > 0 && 1162261467 % n == 0;
    }
    

    5 Integer Limitations with Set

    The idea is similar to #4 but use a set to store all possible power of 3 numbers (from 3^0 to 3^19). It makes code more readable.

    6 Recursive (C++11)

    This solution is specific to C++11, using template meta programming so that complier will replace the call isPowerOf3<Your Input>::cValue with calculated result.

    Time complexity: O(1), space complexity: O(1)

    template<int N>
    struct isPowerOf3 {
        static const bool cValue = (N % 3 == 0) && isPowerOf3<N / 3>::cValue;
    };
    
    template<>
    struct isPowerOf3<0> {
        static const bool cValue = false;
    };
    
    template<>
    struct isPowerOf3<1> {
        static const bool cValue = true;
    };
    
    int main() {
        cout<<isPowerOf3<1162261467>::cValue;
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-02 08:19

    Python solution

    from math import floor
    from math import log
    
    def IsPowerOf3(number):
      p = int(floor(log(number) / log(3)))
      power_floor = pow(3, p)
      power_ceil = power_floor * 3
      if power_floor == number or power_ceil == number:
        return True
      return False
    

    This is much faster than the simple divide by 3 solution.

    Proof: 3 ^ p = number

    p log(3) = log(number) (taking log both side)

    p = log(number) / log(3)

    0 讨论(0)
  • 2020-12-02 08:21

    Another approach is to generate a table on compile time. The good thing is, that you can extend this to powers of 4, 5, 6, 7, whatever

    template<std::size_t... Is>
    struct seq
    {  };
    
    template<std::size_t N, std::size_t... Is>
    struct gen_seq : gen_seq<N-1, N-1, Is...>
    {  };
    
    template<std::size_t... Is>
    struct gen_seq<0, Is...> : seq<Is...>
    {  };
    
    template<std::size_t N>
    struct PowersOfThreeTable
    {
        std::size_t indexes[N];
        std::size_t values[N];
    
        static constexpr std::size_t size = N;
    };
    
    template<typename LambdaType, std::size_t... Is>
    constexpr PowersOfThreeTable<sizeof...(Is)>
        generatePowersOfThreeTable(seq<Is...>, LambdaType evalFunc)
    {
        return { {Is...}, {evalFunc(Is)...} };
    }
    
    template<std::size_t N, typename LambdaType>
    constexpr PowersOfThreeTable<N> generatePowersOfThreeTable(LambdaType evalFunc)
    {
        return generatePowersOfThreeTable(gen_seq<N>(), evalFunc);
    }
    
    template<std::size_t Base, std::size_t Exp>
    struct Pow
    {
        static constexpr std::size_t val = Base * Pow<Base, Exp-1ULL>::val;
    };
    
    template<std::size_t Base>
    struct Pow<Base, 0ULL>
    {
        static constexpr std::size_t val = 1ULL;
    };
    
    template<std::size_t Base>
    struct Pow<Base, 1ULL>
    {
        static constexpr std::size_t val = Base;
    };
    
    constexpr std::size_t tableFiller(std::size_t val)
    { 
        return Pow<3ULL, val>::val;
    }
    
    bool isPowerOfThree(std::size_t N)
    {
        static constexpr unsigned tableSize = 41; //choosen by fair dice roll
    
        static constexpr PowersOfThreeTable<tableSize> table = 
                generatePowersOfThreeTable<tableSize>(tableFiller);
    
        for(auto a : table.values)
            if(a == N)
                return true;
        return false;
    }
    
    0 讨论(0)
  • 2020-12-02 08:22

    Here is a nice and fast implementation of Ray Burns' method in C:

    bool is_power_of_3(unsigned x) {
        if (x > 0x0000ffff)
            x *= 0xb0cd1d99;    // multiplicative inverse of 59049
        if (x > 0x000000ff)
            x *= 0xd2b3183b;    // multiplicative inverse of 243
        return x <= 243 && ((x * 0x71c5) & 0x5145) == 0x5145;
    }
    

    It uses the multiplicative inverse trick for to first divide by 3^10 and then by 3^5. Finally, it needs to check whether the result is 1, 3, 9, 27, 81, or 243, which is done by some simple hashing that I found by trial-and-error.

    On my CPU (Intel Sandy Bridge), it is quite fast, but not as fast as the method of starblue that uses the binary logarithm (which is implemented in hardware on that CPU). But on a CPU without such an instruction, or when lookup tables are undesirable, it might be an alternative.

    0 讨论(0)
  • 2020-12-02 08:22

    I measured times (C#, Platform target x64) for some solutions.

    using System;
    class Program
    {
        static void Main()
        {
            var sw = System.Diagnostics.Stopwatch.StartNew();
            for (uint n = ~0u; n > 0; n--) ;
            Console.WriteLine(sw.Elapsed);              // nada   1.1 s
            sw.Restart();
            for (uint n = ~0u; n > 0; n--) isPow3a(n);
            Console.WriteLine(sw.Elapsed);              // 3^20  17.3 s
            sw.Restart();
            for (uint n = ~0u; n > 0; n--) isPow3b(n);
            Console.WriteLine(sw.Elapsed);              // % /   10.6 s
            Console.Read();
        }
    
        static bool isPow3a(uint n)  // Elric
        {
            return n > 0 && 3486784401 % n == 0;
        }
    
        static bool isPow3b(uint n)  // starblue
        {
            if (n > 0) while (n % 3 == 0) n /= 3;
            return n == 1;
        }
    }
    

    Another way (of splitting hairs).

    using System;
    class Program
    {
        static void Main()
        {
            Random rand = new Random(0); uint[] r = new uint[512];
            for (int i = 0; i < 512; i++)
                r[i] = (uint)(rand.Next(1 << 30)) << 2 | (uint)(rand.Next(4));
            var sw = System.Diagnostics.Stopwatch.StartNew();
            for (int i = 1 << 23; i > 0; i--)
                for (int j = 0; j < 512; j++) ;
            Console.WriteLine(sw.Elapsed);                    //   0.3 s
            sw.Restart();
            for (int i = 1 << 23; i > 0; i--)
                for (int j = 0; j < 512; j++) isPow3c(r[j]);
            Console.WriteLine(sw.Elapsed);                    //  10.6 s
            sw.Restart();
            for (int i = 1 << 23; i > 0; i--)
                for (int j = 0; j < 512; j++) isPow3b(r[j]);
            Console.WriteLine(sw.Elapsed);                    //   9.0 s
            Console.Read();
        }
    
        static bool isPow3c(uint n)
        { return (n & 1) > 0 && 3486784401 % n == 0; }
    
        static bool isPow3b(uint n)
        { if (n > 0) while (n % 3 == 0) n /= 3; return n == 1; }
    }
    
    0 讨论(0)
  • 2020-12-02 08:23

    For really large numbers n, you can use the following math trick to speed up the operation of

      n % 3 == 0
    

    which is really slow and most likely the choke point of any algorithm that relies on repeated checking of remainders. You have to understand modular arithmetic to follow what I am doing, which is part of elementary number theory.

    Let x = Σ k a k 2 k be the number of interest. We can let the upper bound of the sum be ∞ with the understanding that a k = 0 for some k > M. Then

    0 ≡ x ≡ Σ k a k 2 k ≡ Σ k a 2k 2 2k + a 2k+1 2 2k+1 ≡ Σ k 2 2k ( a 2k + a 2k+1 2) ≡ Σ k a 2k + a 2k+1 2 (mod 3)

    since 22k ≡ 4 k ≡ 1k ≡ 1 (mod 3).

    Given a binary representation of a number x with 2n+1 bits as

    x0 x1 x2 ... x2n+1

    where xk ∈{0,1} you can group odd even pairs

    (x0 x1) (x2 x3) ... (x2n x2n+1).

    Let q denote the number of pairings of the form (1 0) and let r denote the number of pairings of the form (0 1). Then it follows from the equation above that 3 | x if and only if 3 | (q + 2r). Furthermore, you can show that 3|(q + 2r) if and only if q and r have the same remainder when divided by 3.

    So an algorithm for determining whether a number is divisible by 3 could be done as follows

     q = 0, r = 0
     for i in {0,1, .., n}
         pair <- (x_{2i} x_{2i+1})
         if pair == (1 0)
             switch(q)
                 case 0:
                     q = 1;
                     break;
                 case 1:
                     q = 2;
                     break;
                 case 2:
                     q = 0;
                     break;
         else if pair == (0 1)
             switch(r)
                 case 0:
                     r = 1;
                     break;
                 case 1:
                     r = 2;
                     break;
                 case 2:
                     r = 0;
     return q == r
    

    This algorithm is more efficient than the use of %.

    --- Edit many years later ----

    I took a few minutes to implement a rudimentary version of this in python that checks its true for all numbers up to 10^4. I include it below for reference. Obviously, to make use of this one would implement this as close to hardware as possible. This scanning technique can be extended to any number that one wants to by altering the derivation. I also conjecture the 'scanning' portion of the algorithm can be reformulated in a recursive O(log n) type formulation similar to a FFT, but I'd have to think on it.

    #!/usr/bin/python
    
    def bits2num(bits):
        num = 0
        for i,b in enumerate(bits):
            num += int(b) << i
        return num
    
    def num2bits(num):
        base = 0
        bits = list()
        while True:
            op = 1 << base
            if op > num:
                break
            bits.append(op&num !=0)
            base += 1
        return "".join(map(str,map(int,bits)))[::-1]
    
    def div3(bits):
    
        n = len(bits)
    
        if n % 2 != 0:
            bits = bits + '0'
    
        n = len(bits)
    
        assert n % 2 == 0
    
        q = 0
        r = 0
        for i in range(n/2):
            pair = bits[2*i:2*i+2]
            if pair == '10':
                if q == 0:
                    q = 1
                elif q == 1:
                    q = 2
                elif q == 2:
                    q = 0
            elif pair == '01':
                if r == 0:
                    r = 1
                elif r == 1:
                    r = 2
                elif r == 2:
                    r = 0
            else:
                pass
    
        return q == r
    
    for i in range(10000):
        truth = (i % 3)  == 0
        bits = num2bits(i)
        check  = div3(bits)
        assert truth == check
    
    0 讨论(0)
提交回复
热议问题