Odious Number


A number is considered to be an odious number if it has an odd number of 1s in its binary expansion. The first 10 odious numbers are 1,2,4,7,10,11,13,14,16,19,21. Interestingly, all powers of 2 are odious since they have only 1 set bit.

The following article discusses 2 approaches in detail to find whether a number is an odious number or not.

Problem Statement

This problem aims to check whether the given number is an odious number i.e. it is a positive number with an odd number of set bits in its binary expansion.

Examples of Odious Number

Input: 34
Output: Non-Odious Number

Explanation

Binary representation of 34 is 10010.

Number of set bits = 2.

Since the number of 1s is even, 34 is not an odious number.

Input: 1024
Output: Odious Number

Explanation

Binary representation of 1024 is 10000000000.

Number of set bits = 1.

Since 1024 is a power of 2, there is only 1 set bit. Hence it is an odious number.

Input: 53
Output: Non-Odious Number

Explanation

(53)10 = (110101)2

Number of set bits = 4.

Hence it is not an odious number.

Solution Approach

In order to determine if a number is odious or not, we must know whether the number of set bits is odd or even. The main task here is to count the number of set bits in the binary expansion of the number. The following techniques can be employed to count the number of bits and then check if the result is odd or even.

Naive Approach

  • Go through all the bits of the number, one by one, using a loop and a right shift operator.

  • If the bit value is 1, increment count by one.

  • Check if the final value of count is odd or even.

  • Display the answer.

Pseudocode

Function no_of_set_bits()

  • Initialise count = 0

  • while (n > 0)

if ((n & 1) > 0)
   Increment count
Right Shift n
  • return count

Function is_odious()

  • if (count is odd)

    • return true

  • else

    • return false

Function main()

  • Initialise n

  • Function call no_of_set_bits()

  • Function call is_odious()

  • Print output

Example: C++ Program

The program checks whether a number is odious or not. It examines the right most bit in every iteration of the loop by right shifting the value of n at the end of each iteration in the function no_of_set_bits().

#include<iostream>
using namespace std;
// this function counts the number of set bits by analyzing the rightmost bit using a while loop till n > 0.
// it performs logical & operation between 1 and n to determine if the rightmost bit is set or not.
// if it is set, count is incremented by 1
// right shift the value of n to make the bit left of the rightmost bit, the new rightmost bit.
int no_of_set_bits(int n){
   int count = 0;
   while (n > 0){
   
      // if the rightmost bit is 1: increment count
      if ((n & 1) > 0){
         count++;
      }
      
      // right shift the value of n to examine the next bit
      n = n >> 1;
   }
   return count;
}
// this function determines if count of set bits is odd or even
// odd -> odious
bool is_odious(int count){

   // if count is odd return true
   if (count % 2 != 0){
      return true;
   }
   return false;
}

// main function
int main(){
   int n = 27;
   int countBits = no_of_set_bits(n);
   if (is_odious(countBits)){
      cout << n << " is Odious Number";
   }
   else {
      cout << n << " is Non-Odious Number";
   }
   return 0;
}

Output

27 is Non-Odious Number

Analysis of Time and Space

Time Complexity: O(log(n)), since binary expansion of n takes log2n bits and we examine all the bits to check for set bits.

Space Complexity: O(1) , as no extra space is used.

Brian Kernighan’s Algorithm Approach

This algorithm can be employed to count the number of set bits for a number in a more efficient way. Function is_odious() can then be used to determine whether the number is odious.

The fundamental principle of this approach is to repeatedly clear the number's rightmost set bit while keeping track of how many iterations are needed to get to zero. The steps involved are −

  • Initialise count to 0

  • While the number is greater than zero, perform bitwise & between the number and its 2’s complement to unset the rightmost set bit.

  • Increment count with each iteration of the loop.

  • Check whether the final count is odd.

  • Display the result.

Example

Let the number be 10. The binary expansion of 10 is 1010. It can be observed that it has 2 set bits.

Loop iteration 1 −

n = 10
n & (n-1) =  10 & 9
1010   (n)
1001   (n - 1)
1000   (n = 8)

Loop iteration 2 −

n = 8
n & (n-1) = 8 & 7
1000    (n)
0111	(n-1)
0       (n = 0) 

Number of iterations = Number of set bits = 2.

Pseudocode

Function no_of_set_bits()

  • Initialise count = 0

  • while (n > 0)

    • n = n & (n-1)

      Increment count

  • return count

Function is_odious()

    Same as in the previous approach

Function main()

    Same as in the previous approach

Example: C++ Program

This program counts the number of set bits by counting the number of iterations needed to unset all the set bits. To unset the bits, we perform bitwise & between n and n - 1. This works because the binary expression for n-1 flips the rightmost set bit of n as well as all bits that follow it.

#include<iostream>
using namespace std;
// this function counts the number of set bits by unsetting the rightmost set bit using a while loop till n > 0.
// it performs logical & operation between n and n - 1 to unset the rightmost set bit.
// count is incremented in every iteration
int no_of_set_bits(int n){
   int count = 0;
   while (n > 0){
      // update the value of n to unset the current rightmost set bit
      n = n & (n - 1);
      count++;
   }
   return count;
}

// this function determines if count of set bits is odd or even
// odd -> odious
bool is_odious(int count){

   // if count is odd return true
   if (count % 2 != 0){
      return true;
   }
   return false;
}

// main function
int main(){
   int n = 27;
   int countBits = no_of_set_bits(n); // function call
   if (is_odious(countBits)){
      cout << n << " is Odious Number";
   }
   else {
      cout << n << " is Non-Odious Number";
   }
   return 0;
}

Output

27 is Non-Odious Number

Time and Space Analysis

Time Complexity − O(log(x)), where x is the number of set bits in the number. If there is only 1 set bit, the loop will run once.

Space Complexity − O(1) , as no extra space is used.

Comparing the Above Approaches

While the first approach is fairly easy to understand, it takes log(n) iterations to produce the final result. The second approach, on the other hand, takes log(x) iterations, where x is the number of set bits in the binary expansion of the number. Hence, it improves the performance.

Conclusion

This article discusses two approaches to check whether a number is odious. It also provides us with the concept of the approach, examples, algorithm used, C++ program solution as well as the complexity analysis of each method. It also draws comparison between the two approaches to figure out which one is more efficient.

Updated on: 17-Aug-2023

135 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements