Perl Weekly Challenge 114: Higher Integer Set Bits

by Abigail

Challenge

You are given a positive integer $N.

Write a script to find the next higher integer having the same number of 1 bits in binary representation as $N.

Examples

Input: $N = 3
Output: 5

Binary representation of $N is 011. There are two 1 bits. So the next higher integer is 5 having the same the number of 1 bits i.e. 101.

Input: $N = 12
Output: 17

Binary representation of $N is 1100. There are two 1 bits. So the next higher integer is 17 having the same number of 1 bits i.e. 10001.

Discussion

We won't be using a naive algorithm by counting upwards from the given number, getting a binary representation, and comparing the number of bits.

Instead, we will be directly constructing the wanted number.

Let the binary representation of the input number be: \(N = \ldots b_{n+m+2+3}b_{n+m+2}01\underbrace{1 \ldots 1}_{n \geq 0} \underbrace{0 \ldots 0}_{m \geq 0}\).

We are interested in the latter part: a 01 followed by \(n \geq 0\) 1 bits, followed by \(m \geq 0\) 0 bits, and nothing more. (If the binary representation doesn't start with a 0, we can always prepend one.)

We now construct a number \(M\) which starts with the same bits as \(N\) (up to bit \(b_{n+m+2}\)), followed by \(10\), followed by \(m\) 0 bits, followed by \(n\) 1 bits. So, \(M = \ldots b_{n+m+2+3}b_{n+m+2}10\underbrace{0 \ldots 0}_{m \geq 0} \underbrace{1 \ldots 1}_{n \geq 0}\).

Clearly, the number of 1 bits in \(N\) equal the number of 1 bits in \(M\). And there will be no other numbers between \(N\) and \(M\) with the same number of bits. Each of the numbers \(P\) between \(N\) and \(M\) are either of the form: \(P = \ldots b_{n+m+2+3}b_{n+m+2}01\underbrace{1 \ldots 1}_{n \geq 0}b_{m} \ldots b_{1}\), with at least one of last \(m\) bits being a 1, or \(P = \ldots b_{n+m+2+3}b_{n+m+2}10\underbrace{0 \ldots 0}_{m \geq 0}b_{n} \ldots b_{1}\), with at least one of last \(n\) bits being a 0. In the former case, the number as too many 1 bits. In the latter case, the number has not enough 1 bits.

Solutions

Perl

This is just a one liner:

say oct sprintf ("0b0%b" => $_) =~ s {01(1*)(0*)$} {10$2$1}r while <>;

We read the number (<>), get a binary representation, prepend with both a 0 and a 0b (sprintf "0b0%b" => $_), perform the substitution described above (s {01(1*)(0*)$} {10$2$1}r), and turn it into a decimal representation again (oct, which takes a binary number as input if its argument starts with 0b).

Find the full program on GitHub.


Please leave any comments as a GitHub issue.