Let's say your number is 13
. In binary, it is 1101
. Let's table it and see if we can see patterns. I'll just insert some line breaks to help later. Also, I'll add 0
, because it doesn't hurt (it has no set bits).
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1011
We can write all the groups under n
like this:
000
001
010
011
100
101
110
111
1000 + 00
1000 + 01
1000 + 10
1000 + 11
1000 + 100 + | (no digits, equal to 0 in sum)
Notice that we have a group of size 2^3
for the 3rd bit of n=1101
; we have a group of size 2^2
for the 2nd bit; and a group of size 2^0
for the LSB. Let's call the group size s=2^b
, where b
is position of all set bits in n
(i.e. b=[3, 2, 0], s=[8, 4, 1]
). Notice bit patterns for the rightmost summand in each group: there's b
columns of bits, and in each column exactly s/2
are set; thus, for each set bit, there are 2^(b-1)*b
set bits in the rightmost columns. But each row also has number of set bits equal to ordinal number of the group: 0, 1, 2 (as we add groups that correspond to set bits in n
), for the total of 2^b*i + 2^(b-1)*b
set bits per group:
Group 0: 2^3*0 + 2^2*3 = 12
Group 1: 2^2*1 + 2^1*2 = 8
Group 2: 2^0*2 + 2^(-1)*0 = 2
This is all for number of set bits up to n-1
. To get number of bits for n
, we need to get one more bit for each bit set in n
itself - which is exactly the number of groups we had. The total is thus 12+8+2 +3 = 25
.
Here it is in Ruby. Note that 2^x * y
is identical to y << x
.
def sum_bits_upto(n)
set_bits = n.to_s(2).reverse.each_char.with_index.map { |c, b|
b if c == "1"
}.compact.reverse
set_bits.each_with_index.sum { |b, i|
(i << b) + (b << b - 1) + 1
}
end
Hopefully I haven't messed up my logic anywhere. BTW, my code says there's 29761222783429247000
bits from 1
to 1_000_000_000_000_000_000
, with just 24 iterations of the loop. That should be fast enough :)
EDIT: Apparently I have goldfish memory. The sum is monotonously increasing (with each successive number, there is a positive number of bits added to the running total). That means, we can use binary search, which should zero in on the target superquick, even if we don't help it by storing interim results (this executes in 0.1s on my Mac):
max = 1_000_000_000_000_000_000_000_000_000
k = 1_000_000_000_000_000_000
n = (1..max).bsearch { |n|
sum_bits_upto(n) >= k
}
# => 36397208481162321
There has to be a nice formula to calculate the theoretically possible max n to search for based on k
, but I couldn't be bothered, so I just put in something big enough. bsearch
is that good.
EDIT2: couple of tweaks for the bsearch
condition, messed it up at first.