Способ решения подобных проблем состоит в том, чтобы выписать первые несколько значений и найти шаблон
Number binary # bits set F(n)
1 0001 1 1
2 0010 1 2
3 0011 2 4
4 0100 1 5
5 0101 2 7
6 0110 2 9
7 0111 3 12
8 1000 1 13
9 1001 2 15
10 1010 2 17
11 1011 3 20
12 1100 2 22
13 1101 3 25
14 1110 3 28
15 1111 4 32
Требуется немного посмотреть, но, подумав, вы заметили, что двоичные представления первых 8 и последних 8 чисел абсолютно одинаковы, за исключением того, что первые 8 имеют 0
в MSB (старший значащий бит) , в то время как последние 8 имеют 1
. Таким образом, например, чтобы вычислить F(12)
, мы можем просто взять F(7)
и добавить к нему количество установленных битов в 8, 9, 10, 11 и 12. Но это то же самое, что число установленных битов в 0 , 1, 2, 3 и 4 (т. Е. F(4)
) , плюс еще один для каждого номера!
# binary
0 0 000
1 0 001
2 0 010
3 0 011
4 0 100
5 0 101
6 0 110
7 0 111
8 1 000 <--Notice that rightmost-bits repeat themselves
9 1 001 except now we have an extra '1' in every number!
10 1 010
11 1 011
12 1 100
Таким образом, для 8 <= n <= 15
, F(n) = F(7) + F(n-8) + (n-7)
. Точно так же можно отметить, что для 4 <= n <= 7
, F(n) = F(3) + F(n-4) + (n-3)
; и для 2 <= n <= 3
, F(n) = F(1) + F(n-2) + (n-1)
. В общем, если мы установим a = 2^(floor(log(n)))
, то F(n) = F(a-1) + F(n-a) + (n-a+1)
Это не совсем дает нам алгоритм O(log n)
; однако сделать это легко. Если a = 2^x
, то обратите внимание в таблице выше, что для a-1
первый бит установлен ровно a/2
раз, второй бит установлен ровно a/2
раз, третий бит ... вплоть до х-й бит Таким образом, F(a-1) = x*a/2 = x*2^(x-1)
. В приведенном выше уравнении это дает нам
F(n) = x*2<sup>x-1</sup> + F(n-2<sup>x</sup>) + (n-2<sup>x</sup>+1)
Где x = floor(log(n))
. Каждая итерация вычисления F
будет по существу удалять MSB; таким образом, это алгоритм O(log(n))
.