Let S(n) be the set of numbers 0 to n (with no duplicates, but in any order). Then S(2n+1) = {2*s for s in S(n)} + {2*s+1 for s in S(n)}, and S(2n) = {2*s for s in S(n)} + {2*s+1 for s in S(n-1)}.
Two examples:
S(7) = {2*s for s in S(3)} + {2*s+1 for s in S(3)}
= {0, 2, 4, 6} + {1, 3, 5, 7}
S(10) = {2*s for s in S(5)} + {2*s+1 for s in S(4)}
= {0, 2, 4, 6, 8, 10} + {1, 3, 5, 7, 9}
Letting a(n) be defined to be the total of bits set in all the numbers in S(n), and using the formulae for S, we have a(2n+1) = 2a(n) + n+1, and a(2n) = a(n) + a(n-1) + n. That's because the number of bits set in {2*s for s in S(n)} is the same number as the number of bits set in S(n), and the number of bits set in {2*s+1 for s in S(n)} is the number of bits set in S(n) plus one for every element of S(n) (namely: n+1).
Those same equations appear on https://oeis.org/A000788, credited to Ralf Stephan:
a(0) = 0
a(2n) = a(n)+a(n-1)+n
a(2n+1) = 2a(n)+n+1
Using this, one can write a function B with B(N) = a(N), a(N-1):
def B(N):
if N == 0:
return 0, 0
r, s = B(N//2)
if N % 2:
return 2*r+N//2+1, r+s+N//2
else:
return r+s+N//2, 2*s+N//2
The double return value is a form of dynamic programming, avoiding recomputing the same values multiple times.
The second return value is the one you're interested in. For example:
>> print(B(7)[1])
9
>> print(B(28)[1])
64
>> print(B(10**20)[1])
3301678091638143975424
This obviously runs in O(log N) arithmetic operations, and uses O(log N) stack.
Getting to constant space complexity
One can reduce the space complexity to O(1) with a little care.
We can write the Ralf Stephan equations in a matrix times vector form:
[ a(2n+1) ] = [2 0 1 1] [ a(n) ]
[ a(2n) ] [1 1 1 0] * [ a(n-1)]
[ 2n+1 ] [0 0 2 1] [ n ]
[ 1 ] [0 0 0 1] [ 1 ]
and
[ a(2n) ] = [1 1 1 0] [ a(n) ]
[ a(2n-1) ] [0 2 1 0] * [ a(n-1)]
[ 2n ] [0 0 2 0] [ n ]
[ 1 ] [0 0 0 1] [ 1 ]
Repeatedly applying one or the other of these rules, gives:
[ a(n) ] = M[0] * M[1] * ... * M[k] * [ a(0) ]
[ a(n-1)] [ a(-1)]
[ n ] [ 0 ]
[ 1 ] [ 1 ]
Where the M[0], M[1], ..., M[k] are one or the other of the two 4x4 matrices that appear in the matrix-times-vector versions of the Ralf Stephan equations, depending on the kth bit of n.
Thus:
def mat_mul(A, B):
C = [[0] * 4 for _ in range(4)]
for i in range(4):
for j in range(4):
for k in range(4):
C[i][k] += A[i][j] * B[j][k]
return C
M1 = [[2, 0, 1, 1], [1, 1, 1, 0], [0, 0, 2, 1], [0, 0, 0, 1]]
M0 = [[1, 1, 1, 0], [0, 2, 1, 0], [0, 0, 2, 0], [0, 0, 0, 1]]
def B2(N):
M = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]
while N:
M = mat_mul(M, M1 if N%2 else M0)
N >>= 1
return M[1][3]
The function B2 performs O(log n) arithmetic operation, but uses constant space.
We can do a little better, by noting that the M matrix is always of the form:
[ a b c d ]
[ a-1 b+1 c e ]
[ 0 0 a+b a-1 ]
[ 0 0 0 1 ]
Then, B3 performs the matrix multiplies of B2 in an optimized way, depending on the observed structure of M:
def B3(N):
a, b, c, d, e = 1, 0, 0, 0, 0
while N:
if N%2:
a, c, d, e = 2*a+b, a+b+2*c, a+c+d, a+c+e-1
else:
b, c = a+2*b, a+b+2*c
N >>= 1
return e
This is as good as this approach can take us: the only arithmetic operations are additions, multiply by two, divide by two, and testing the lowest bit. The space complexity is constant. Even for huge N (for example, 10^200), the time taken is negligible.
Fast version in C.
For speed, the C version (using gcc's extension of __int128) computes b3(10**20) in approximately 140 nanoseconds on my machine. The code is a straightforward conversion of the B3 python function (noting that d isn't needed), slightly hindered by the lack of multiple assignment in C.
typedef unsigned __int128 uint128;
uint128 b3(uint128 n) {
uint128 a=1, b=0, c=0, e=0;
while (n) {
if (n&1) {
e = a+c+e-1;
c = a+b+2*c;
a = 2*a+b;
} else {
c = a+b+2*c;
b = a+2*b;
}
n >>= 1;
}
return e;
}