Algorithm to calculate the number of 1s for a range of numbers in binary
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
If the goal is to count how many 1 bits appear in all numbers across a range, iterating through every integer and counting bits one by one is correct but often too slow. A much better approach is to compute the total number of set bits from 0 to n, then subtract two prefix totals to get any range answer.
The Slow but Obvious Solution
The direct method is:
- loop from
lefttoright - count the
1bits of each value - sum them
That works, but the complexity is roughly O((right - left + 1) * word_size). For small ranges it is fine. For large ranges, it wastes structure that binary numbers naturally provide.
Use a Prefix Function
Define count_ones_upto(n) as the total number of set bits in all integers from 0 through n. Then the answer for any inclusive range is:
count_ones_upto(right) - count_ones_upto(left - 1)
That turns the range problem into a faster prefix problem.
Key Observation About Powers of Two
Consider all numbers from 0 to 2^k - 1. Across that full block, each bit position is 1 exactly half the time. There are 2^k numbers and k bit positions, so the total number of set bits in that block is:
k * 2^(k - 1)
That identity is the basis of the fast recursive or iterative solution.
Derive the Recurrence
Suppose n is positive and the highest power of two not exceeding n is p = 2^k.
Then the range 0..n can be split into:
- '
0..p-1' - '
p..n'
The first block contributes k * 2^(k - 1) set bits.
In the second block:
- the most significant bit is set in every number, contributing
n - p + 1 - the remaining lower bits behave exactly like the numbers
0..(n - p)
So:
count_ones_upto(n) = k * 2^(k - 1) + (n - p + 1) + count_ones_upto(n - p)
That reduces the problem quickly because each step removes the top bit.
Python Implementation
This runs in O(log n) time for the prefix function because each loop step removes one highest set bit.
Why It Works on a Sample
Take the range 5..7:
- '
5is101, so it has2ones' - '
6is110, so it has2ones' - '
7is111, so it has3ones'
The total is 7.
Using the prefix function:
- '
count_ones_upto(7)is12' - '
count_ones_upto(4)is5' - '
12 - 5 = 7'
The same answer appears without scanning every value in the target range.
When Per-Number Counts Are Needed
Sometimes the problem is not the total across the range, but the popcount of each number from 0 to n. In that case, dynamic programming is often better:
That solves a related problem efficiently, but it is a different output shape from the single total count.
Common Pitfalls
- Mixing up "count the ones in each number" with "count the total ones across the whole range" leads to the wrong algorithm.
- Forgetting that the range is inclusive causes off-by-one errors in the subtraction formula.
- Using the fast prefix recurrence without handling
left = 0carefully can produce negative-index style mistakes in surrounding code. - Recomputing bit counts for every integer is acceptable for tiny ranges, but it does not scale.
- Confusing the highest power of two with the highest set bit position leads to incorrect recurrence terms.
Summary
- The fastest general approach is to compute total set bits from
0..n, then subtract prefix totals. - The core recurrence comes from how often each bit is set in a full power-of-two block.
- '
count_ones_upto(n)can be implemented inO(log n)time.' - For per-number popcounts, use a different dynamic-programming approach instead of the range-total formula.

