Finding odd divisors with bit-shifting

Viewed 659

(This question refers to this Codeforces problem) I want to find out if any n (2 ≤ n ≤ 10¹⁴) has an odd divisor or not. Using C++11, I thought it would be possible to brute force a solution by iterating through every odd number until n, and checking with % if it is divisible or not. Something such as:

for(unsigned long long i=3;i<n;i+=2){
   if(n%i==0) return true; //It has an odd divisor
}
return false; //n%j==0 was never true so it doesn't have an odd divisor

Which of course turned out to be extremely slow if any big number were given. I found out people were solving it by bit-shifting it, and checking if the last bit was 1, something similar to this:

while(!(n&1))n>>=1;
if(n==1) return false; else return true;

If I am not mistaken, this last piece of code is checking if it's odd with the last bit (n&1) by performing n=n/2ⁿ. How does computing n=n/2ⁿ have the same accuracy in regards solving the problem as checking it for every odd number? (How do I know that I am not ''skipping'' a divisor?).

5 Answers

To understand this, you have to understand how rightmost bit of odd and even number works.
Basically any odd number has it's 0th bit(rightmost) as 1(remember bit counting start from right, i.e right most bit is 0th bit, then you go left one by one i.e 1st bit, 2nd bit and so on) and any even number has it's 0th bit as 0.
so when you do

int value = n & 1;

what are you doing is that you are doing 'AND' of n & 1, so if the n is odd, then it's 0th bit will be 1, so when you do suppose for example

int result = 5 & 1;

now this actually look like this

(101) & (001)

so you get 1 as a result(since all other bits of 1 are 0, so the result of AND of all those other bits of 5 & 1 will be 0, so basically you remain with 1), but if n is even, then it's 0th bit will be 0, for example

int result = 10 & 1;

will look like this

1010 & 0001

now based on previous example, you can see that the result will be always be 0 in case of even numbers. This is why checking if a number is odd or even is much faster this way as you don't go in all modulo and other higer level stuff, you are doing stuff in low level here.

So coming to the main point, what is happening is that we have to check if a number contains any odd divisor or not, let us see it step by step
A number is a nothing but multiple of it's divisors, for example

30 = 10 * 3;
30 = 15 * 2;
30 = 5 * 6

but if you will look closer, you will find that it is always a combination of same unique prime numbers i.e

30 = 2 * 5 * 3;
30 = 3 * 5 * 2;
30 = 5 * 2 * 3;

So now let's see a fact among prime number, we see that 2 is the only prime number that is not odd.
If we go with this fact, we can be sure if 2 is multiplied by any other prime number , then that resultant number will always be an odd number(and remember even * odd = odd). so if we want a number that doesn't have any odd divisors, then we don't want any odd prime number in it's multiplication combination, then that number should be combination of 2's multiplication only, in other terms power of 2. (we can ignore case of 1, as it is provided in the problem to not consider that as input)

so If we just a check a number if it's is a power of 2 or not, our problem is solved, i.e if it is power of 2, then it doesn't contain any odd divisor, else it contain a odd divisor.
now how do we find if a number is a power of 2 or not.
so any number we see can be a combination of different bit in it's binary representation
for example

10 = (1010) (2^3 * 1 + 2^2 * 0 + 2^1 * 1 + 2^0 * 0)

if any number is a just power of 2, then it will have only single bit as 1 in it's binary representation, all other bits will be 0 ( for case of 2^0 = 1 , you can see that we have been given constraint that x > 1, so we can ignore case handling for 1)

so now let's look at this code

// it runs the while loop until all the right bits of first 1 bit is 0, that is it runs till we shift our rightmost 1 bit to 0th bit
while(!(n & 1)) { 
    n >>= 1; // if we are inside the loop, it means rightmost 1 is still not at 0th bit position, as the AND is done for 0th bit like I explained above, so doing this will right shift our rightmost 1, i.e if it was at 2nd position, it will now come at 1st position 
}

if(n == 1) {
   // if it is 1, it means only 1 bit is there in whole binary representation of the number which is at 0th position, it means it is a power of two(remember that this n is calculated based on sum of all the power of 2)
    return false; 
} else {
    return true;
}

This more optimized solution effectively strips out any powers of two and if the remainder isn't a factor of 1 it must involve some other factor.

Since all even factors have 2 as factor themselves, this just "boils" the number down until there's either a remainder factor, or 1.

Like, as an example:

0b11010 (26) -> 0b1101 (13)
0b11000 (24) -> 0b11 (3)
0b10000 (16) -> 0b1 (1)

Where you could also have several prime factors, as in:

0b1111110000 (1008) -> (63)

That's 9 x 7, but you could just consider 63 to be the only non-even factor just the same.

!(n & 1) is equivalent to n & 1 == 0, and tests whether n is even, because n & 1 is 1 for odd numbers and 0 for even numbers.
n >>= 1 does integer division by two.
(As a side note, compilers have been perfectly capable of doing this optimization of n % 2 == 0 and n /= 2 for decades, and are much better than humans at determining when it is actually beneficial.)

Next, maths:

The smallest odd divisor is also the smallest odd prime factor.

If x is odd, we know that it has at least one odd prime factor, and thus at least one odd divisor.

If x is even, it can be written 2 * y, where y is x/2.
We also know that we can write x as the product of its prime factors, 2 * f0 * f1 * ... * fn.
Thus, y is f0 * f1 * ... * fn, and the odd divisors of x/2 are exactly the same as the odd divisors of x.

Repeat with x/2 until you have reached either 1 or an odd number greater than 2.

The problem itself have some ambiguities.
The answer to the problem is trivially true in all cases, since the odd number 1 is a divisor of any integer number n.
Thus, I suspect that the intent is to find some odd divisor different of 1.

By the Fundamental Theorem of Arithmetic, a positive integer number can be decomposed in a unique manner as the product of all prime numbers rised to some non-negative integer power.

For example, the number 600 is decomposed as the product of 2*2*2*3*5*5.
The only even primer number is 2, so that, after dividing n by all factors equal to 2 of its prime decomposition, only odd prime factors will remain.

If n had some odd divisor different of 1, then dividing n by 2 as many times as possible, we would obtain the greatest odd number dividing n.

On the other hand, dividing by 2 is equivalent to shifting by 1:
(n/2) == (n >> 1)

Thus, it is enough to obtain such maximal odd divisor by shifting as many times as possible.

int odd_divisor = n;
while (odd_divisor % 2 == 0)
  odd_divisor /= 2;
return odd_divisor;  // This number is odd,
                     // it is a divisor of n,
                     // and do with it
                     // whatever you want.

If the number odd_divisor == 1 it means that the only odd divisor of n is 1, hence the answer to the problem in this case seems to be false.
Otherwise, the answer is true.

You can replace the operation odd_divisor /= 2
by odd_divisor >>= 1,
and also you can replace odd_dividor % 2 == 0
by the condition odd_dividor & 1 == 0.

As other have already mentioned, the representation of an integer number in memory is done in base 2, so that to check the last bit of it is equivalent to check for its parity.

The numbers without odd divisors are all the powers of 2. Using the AND bitwise operator you could do something like this:

    if ((n & (n - 1)) == 0) {
        cout << "NO" << endl;
    }
    else {
        cout << "YES" << endl;
    }

In binary, all powers of two have only a single bit as 1 in its binary representation, all other bits will be 0. Also, the number before a power of two will have all its bits as 1. So with the ((n & (n - 1)) == 0) you can easily identify if a number is a power of two.

Related