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

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

I saw this question, and pop up this idea.

23条回答
  •  不思量自难忘°
    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
    

提交回复
热议问题