Число единичек в двоичном представлении числа

Дано целочисленное число N без знака. Нужно подсчитать количество единичек в его двоичном представлении.

Например, число 11 в двоичном представлении выглядит 1011. Значит ответ будет 3.

Идея решения: если самый правый бит равен 1, то увеличить счётчик единичек. И в любом случае (0 или 1 справа) сдвинуть битовое представление на 1 позицию вправо, заменяя свободные биты слева нулями. Повторять до тех пор, пока результат сдвига не станет равен нулю.

Компилируемый код идеи решения:
     int sum=0;
        while(N!=0) {
            if ((N&1)==1) sum++;
            N = N >>> 1;
        }

Проблема в том, что для больших чисел такой подсчёт может быть непозволительно длинным.

Ускорим алгоритм, находя сразу позицию p самого правого ненулевго бита и сдвигая число N на число позиций p:

int sum = 0; while (N!=0) { double position = 1 + Math.log(N & ~(N-1))/Math.log(2); if (position!=0) sum++; n >>>= (int)position; } return sum;

Поясним метод подсчёта position. Возьмём N=28.
Его двоичное представление = 11100. Это число X.
Двоичное представление 28-1=27 будет : 11011
Двоичное представление ~27 (инвертирование всех битов) : 00100 (с единичками впереди 11111111111111111111111111100100) Это число Y.
И подсчитав побитово X&Y мы получим : 100 в двоичной системе. Или 4 в десятичной.
Разложение 4 в двоичную систему происходит по фомуле 1*2^2 + 0*2^1 + 0*2^0.
Или 4 = 2^2. (Z)
Если прологарифмировать обе части по основанию 2, то равенство Z сохранится:
log2(4) = log2(2^2).
Пользуясь свойством стпени в логарифме: log2(2^2)=2*log2(2).
А log2(2)=1. Поэтому в итоге log2(4)=2. Это на 1 меньше, чем ожидаемоая позиция 3. Поэтому добаляем +1.
Проблема в том, что в библиотеке Java нет двоичного логарифма, а есть только натуральный.
Но у логарифма есть свойства  log2(a) = logn(a)/logn(2).

Поэтому в итоге подсчёт значения  position сводится к тому, что:
  1. мы подсчитываем число Q, которое начинается с самого правого бита исходного N.
  2. число Q мы логарифмируем, по основанию 2, чтобы подсчитать position.
  3. для того, чтобы получить основание 2, мы делаем Math.log(Q)/Math.log(2)+1.
Запомнить: 
  • формула для получения двоичного представления, в котором включен только самый правый бит исходного числа N & ~(N-1)
  • формула позиции самого правого бита: 1 + Math.log(N & ~(N-1))/Math.log(2)

Комментарии

Популярные сообщения из этого блога

Разбиение числа на сумму с максимальным произведнием слагаемых

Число подмассивов, в которых сумма элементов равна k

Сложить два числа, которые представлены списком