Facebook Pixel

762. Prime Number of Set Bits in Binary Representation

EasyBit ManipulationMath
Leetcode Link

Problem Description

You need to find how many numbers in a given range have a special property: the count of 1s in their binary representation must be a prime number.

Given two integers left and right, count all numbers from left to right (inclusive) where the number of 1 bits in the binary form is prime.

For example:

  • The number 21 in binary is 10101
  • It has three 1 bits (at positions 1, 3, and 5)
  • Since 3 is a prime number, 21 would be counted

The solution works by:

  1. Creating a set of prime numbers up to 19 (since the maximum possible bit count for 32-bit integers is around 20)
  2. For each number in the range [left, right], using bit_count() to count the 1 bits
  3. Checking if this count exists in the prime set
  4. Summing up all numbers that satisfy the condition

The key insight is that we only need to check primality for small numbers (up to about 20) since that's the maximum number of set bits possible in the given constraints.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The first observation is that we need to count set bits (1s) in binary representation for each number in our range. Since we're dealing with 32-bit integers, the maximum number of set bits any number can have is 32.

This leads to a crucial insight: we only need to determine which numbers from 0 to 32 are prime. This is a small, fixed set that we can precompute. The prime numbers up to 32 are: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31.

In practice, for the typical constraints of this problem (numbers up to around 10^6), we'll never see numbers with more than 20 set bits. For example, 2^20 - 1 = 1,048,575 has 20 set bits and is already over a million. This means we can optimize our prime set to only include primes up to 19 or 20.

The straightforward approach becomes:

  1. For each number in the range, count its set bits
  2. Check if that count is prime
  3. Keep a running total

Since checking if a small number (≤32) is prime is O(1) using a precomputed set, and counting bits is also O(1) with built-in functions like bit_count(), the entire solution is linear in the size of the range.

The elegance of this solution comes from recognizing that we're not checking if the numbers themselves are prime (which would be expensive), but rather checking if the bit count is prime - and bit counts are always small numbers that we can handle efficiently with precomputation.

Learn more about Math patterns.

Solution Approach

The implementation uses a direct counting approach with precomputed primes:

Step 1: Precompute Prime Numbers

primes = {2, 3, 5, 7, 11, 13, 17, 19}

We create a set containing all prime numbers up to 19. Using a set gives us O(1) lookup time when checking if a bit count is prime. The number 19 is sufficient because numbers in the typical constraint range won't have more than 19-20 set bits.

Step 2: Iterate Through the Range

for i in range(left, right + 1)

We examine each number from left to right inclusive. The + 1 ensures we include the right boundary.

Step 3: Count Set Bits and Check Primality

i.bit_count() in primes

For each number i:

  • i.bit_count() returns the number of 1s in the binary representation
  • We check if this count exists in our prime set
  • This expression evaluates to True (counted as 1) or False (counted as 0)

Step 4: Sum the Results

sum(i.bit_count() in primes for i in range(left, right + 1))

The generator expression produces a sequence of boolean values (True/False). Python's sum() function treats True as 1 and False as 0, giving us the total count of numbers with a prime number of set bits.

Time Complexity: O(n) where n = right - left + 1, since we check each number once and both bit_count() and set lookup are O(1) operations.

Space Complexity: O(1) as we only store a fixed-size set of primes regardless of input size.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with left = 6 and right = 10.

Step 1: Set up our prime numbers

primes = {2, 3, 5, 7, 11, 13, 17, 19}

Step 2: Check each number in the range [6, 10]

For i = 6:

  • Binary representation: 110
  • Count of 1s: 2
  • Is 2 prime? Yes (2 is in our prime set)
  • Count this number ✓

For i = 7:

  • Binary representation: 111
  • Count of 1s: 3
  • Is 3 prime? Yes (3 is in our prime set)
  • Count this number ✓

For i = 8:

  • Binary representation: 1000
  • Count of 1s: 1
  • Is 1 prime? No (1 is not in our prime set)
  • Don't count this number ✗

For i = 9:

  • Binary representation: 1001
  • Count of 1s: 2
  • Is 2 prime? Yes (2 is in our prime set)
  • Count this number ✓

For i = 10:

  • Binary representation: 1010
  • Count of 1s: 2
  • Is 2 prime? Yes (2 is in our prime set)
  • Count this number ✓

Step 3: Sum up the results

  • Numbers with prime bit counts: 6, 7, 9, 10
  • Total count: 4

The solution code would execute this as:

primes = {2, 3, 5, 7, 11, 13, 17, 19}
result = sum(i.bit_count() in primes for i in range(6, 11))
# Generates: True, True, False, True, True
# Sum: 1 + 1 + 0 + 1 + 1 = 4

Solution Implementation

1class Solution:
2    def countPrimeSetBits(self, left: int, right: int) -> int:
3        """
4        Count numbers in range [left, right] that have a prime number of set bits.
5      
6        Args:
7            left: Lower bound of the range (inclusive)
8            right: Upper bound of the range (inclusive)
9          
10        Returns:
11            Count of integers with prime number of set bits
12        """
13        # Set of prime numbers up to 19 (maximum possible bit count for 32-bit integers)
14        # Since right <= 10^6 < 2^20, we only need primes up to 19
15        prime_numbers = {2, 3, 5, 7, 11, 13, 17, 19}
16      
17        # Count how many numbers have prime number of set bits
18        count = 0
19        for num in range(left, right + 1):
20            # Get the number of set bits (1s) in binary representation
21            set_bits_count = num.bit_count()
22          
23            # Check if the count of set bits is a prime number
24            if set_bits_count in prime_numbers:
25                count += 1
26              
27        return count
28
1class Solution {
2    // Set containing all prime numbers up to 19 (maximum possible bit count for 32-bit integer)
3    private static final Set<Integer> PRIME_NUMBERS = Set.of(2, 3, 5, 7, 11, 13, 17, 19);
4
5    /**
6     * Counts how many integers in the range [left, right] have a prime number of set bits
7     * in their binary representation.
8     * 
9     * @param left  the starting value of the range (inclusive)
10     * @param right the ending value of the range (inclusive)
11     * @return the count of numbers with prime number of set bits
12     */
13    public int countPrimeSetBits(int left, int right) {
14        int primeSetBitCount = 0;
15      
16        // Iterate through each number in the given range
17        for (int currentNumber = left; currentNumber <= right; currentNumber++) {
18            // Get the count of set bits (1s) in binary representation
19            int setBitCount = Integer.bitCount(currentNumber);
20          
21            // Check if the bit count is a prime number
22            if (PRIME_NUMBERS.contains(setBitCount)) {
23                primeSetBitCount++;
24            }
25        }
26      
27        return primeSetBitCount;
28    }
29}
30
1class Solution {
2public:
3    int countPrimeSetBits(int left, int right) {
4        // Set containing all prime numbers up to 19
5        // (maximum possible set bits in a 32-bit integer is 32, but given constraints only need up to 19)
6        unordered_set<int> primes{2, 3, 5, 7, 11, 13, 17, 19};
7      
8        // Initialize counter for numbers with prime count of set bits
9        int count = 0;
10      
11        // Iterate through all numbers in the range [left, right]
12        for (int num = left; num <= right; ++num) {
13            // Count the number of set bits (1s) in binary representation
14            int setBitsCount = __builtin_popcount(num);
15          
16            // Check if the count of set bits is a prime number
17            // If yes, increment the counter
18            if (primes.count(setBitsCount)) {
19                count++;
20            }
21        }
22      
23        return count;
24    }
25};
26
1function countPrimeSetBits(left: number, right: number): number {
2    // Set containing all prime numbers up to 19
3    // (maximum possible set bits in a 32-bit integer is 32, but given constraints only need up to 19)
4    const primes = new Set<number>([2, 3, 5, 7, 11, 13, 17, 19]);
5  
6    // Initialize counter for numbers with prime count of set bits
7    let count = 0;
8  
9    // Iterate through all numbers in the range [left, right]
10    for (let num = left; num <= right; num++) {
11        // Count the number of set bits (1s) in binary representation
12        let setBitsCount = 0;
13        let temp = num;
14      
15        // Count set bits using bit manipulation
16        while (temp > 0) {
17            setBitsCount += temp & 1;
18            temp = temp >>> 1; // Use unsigned right shift
19        }
20      
21        // Check if the count of set bits is a prime number
22        // If yes, increment the counter
23        if (primes.has(setBitsCount)) {
24            count++;
25        }
26    }
27  
28    return count;
29}
30

Time and Space Complexity

Time Complexity: O(n) where n = right - left + 1

The algorithm iterates through each number from left to right inclusive, which gives us n iterations. For each number i in this range:

  • i.bit_count() operation takes O(1) time in Python (implemented as a built-in method that counts set bits efficiently)
  • Checking membership in the primes set takes O(1) average time
  • The sum operation accumulates these results

Therefore, the overall time complexity is O(n) * O(1) = O(n).

Space Complexity: O(1)

The space usage consists of:

  • The primes set containing exactly 8 elements (all prime numbers less than 20, since the maximum number of bits in a 32-bit integer is 32, and we only need primes up to 19), which takes O(1) space
  • The generator expression in the sum function doesn't create an intermediate list, it processes elements one at a time, using O(1) additional space
  • Variables for iteration and counting use O(1) space

The space complexity remains constant regardless of the input range size, making it O(1).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrectly Determining the Range of Prime Numbers Needed

A common mistake is either using too few primes (missing valid cases) or calculating primes dynamically (inefficient). Developers might assume they need primes up to 32 (thinking of 32-bit integers) or might not realize the actual maximum bit count for their input constraints.

Pitfall Example:

# Wrong: Missing necessary primes
primes = {2, 3, 5, 7}  # Too few primes

# Wrong: Overcomplicated prime generation
def is_prime(n):
    if n < 2: return False
    for i in range(2, int(n**0.5) + 1):
        if n % i == 0: return False
    return True

# Checking primality for each bit count repeatedly
for num in range(left, right + 1):
    if is_prime(num.bit_count()):
        count += 1

Solution: Precompute the exact set of primes needed based on constraints. For numbers up to 10^6 (approximately 2^20), the maximum bit count is 20, so primes up to 19 are sufficient:

primes = {2, 3, 5, 7, 11, 13, 17, 19}

2. Using Manual Bit Counting Instead of Built-in Methods

Developers might implement their own bit counting logic, which is error-prone and less efficient than built-in methods.

Pitfall Example:

# Wrong: Manual bit counting with potential errors
def count_bits(n):
    count = 0
    while n > 0:
        count += n & 1
        n = n >> 1  # Could forget this shift
    return count

# Or using string conversion (less efficient)
bin_str = bin(num)[2:]  # Remove '0b' prefix
bit_count = bin_str.count('1')

Solution: Use Python's built-in bit_count() method (Python 3.10+) or bin(num).count('1') for older versions:

# Python 3.10+
set_bits = num.bit_count()

# Python < 3.10
set_bits = bin(num).count('1')

3. Off-by-One Errors with Range Boundaries

Forgetting that the range should be inclusive of both boundaries is a frequent mistake.

Pitfall Example:

# Wrong: Excludes the right boundary
for num in range(left, right):  # Missing right + 1
    # ... processing

# Wrong: Including unnecessary numbers
for num in range(left - 1, right + 2):  # Too broad
    # ... processing

Solution: Always use range(left, right + 1) to include both boundaries:

for num in range(left, right + 1):
    # Process each number in [left, right] inclusive

4. Inefficient Prime Checking with Lists Instead of Sets

Using a list for prime storage leads to O(n) lookup time instead of O(1).

Pitfall Example:

# Wrong: Using list leads to slower lookups
primes = [2, 3, 5, 7, 11, 13, 17, 19]
if set_bits_count in primes:  # O(n) lookup
    count += 1

Solution: Use a set for constant-time lookups:

primes = {2, 3, 5, 7, 11, 13, 17, 19}  # Set for O(1) lookup
if set_bits_count in primes:
    count += 1
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

You are given an array of intervals where intervals[i] = [start_i, end_i] represent the start and end of the ith interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More