Подсчёт количества ненулевых бит в числе
От: DemAS http://demas.me
Дата: 16.05.03 07:22
Оценка:
Добрый день.

Интересует реализация такого алгоритма.

Честно говоря, одна из реализаций у меня есть, но я ее приведу в следующем сообщении. Может кто-то захочет попробовать реализовать сам, а не срезу смотреть ответ.
Re: Подсчёт количества ненулевых бит в числе
От: DemAS http://demas.me
Дата: 16.05.03 07:24
Оценка: 50 (2)
А вот, собственно, одна из реализаций:


        #define g21 0x55555555ul // = 0101_0101_0101_0101_0101_0101_0101_0101
        #define g22 0x33333333ul // = 0011_0011_0011_0011_0011_0011_0011_0011
        #define g23 0x0f0f0f0ful // = 0000_1111_0000_1111_0000_1111_0000_1111

        v = (v & g21) + ((v >> 1) & g21);
        v = (v & g22) + ((v >> 2) & g22);
        v = (v + (v >> 4)) & g23;
        return (v + (v >> 8) + (v >> 16) + (v >> 24)) & 0x3f;



Но я абсолютно не понимаю, как она работает. Что это за магические коэффициенты ?

Если кто-то мне объяснит — буду благодарен.
Re: Подсчёт количества ненулевых бит в числе
От: UgN  
Дата: 16.05.03 07:30
Оценка: 22 (2)
Здравствуйте, DemAS, Вы писали:


Подсчёт количества ненулевых бит в числе v за log2(v) проходов (на примере 8-битного числа): 
    v = (v & 0x55) + ((v >> 1) & 0x55);
    v = (v & 0x33) + ((v >> 2) & 0x33);
    return (v & 0x0f) + ((v >> 4) & 0x0f); 

Число разбивается на группы бит одной длины (сперва по одному биту). Затем значения соседних пар одновременно складываются и сохраняются в новых группах в два раза большей длины до тех пор, пока не будут сложены половинки числа. 
Поскольку длина суммы двух чисел равна длине большего числа с битом для переноса, поэтому для каждой группы в маске групп достаточно иметь единиц не больше номера шага (то есть 1+log2 от длины группы). Hапример, для подсчёта единичных бит в 32-битном числе (с оптимизацией): 

#define g21 0x55555555ul // = 0101_0101_0101_0101_0101_0101_0101_0101
#define g22 0x33333333ul // = 0011_0011_0011_0011_0011_0011_0011_0011
#define g23 0x0f0f0f0ful // = 0000_1111_0000_1111_0000_1111_0000_1111

v = (v & g21) + ((v >gt; 1) & g21);
v = (v & g22) + ((v >gt; 2) & g22);
v = (v + (v >gt; 4)) & g23;
return (v + (v >gt; 8) + (v >gt; 16) + (v >gt; 24)) & 0x3f;

Для сокращения количества шагов можно суммировать тройки и т.д.: 
#define g31 0x49249249ul // = 0100_1001_0010_0100_1001_0010_0100_1001
#define g32 0x381c0e07ul // = 0011_1000_0001_1100_0000_1110_0000_0111

v = (v & g31) + ((v >> 1) & g31) + ((v >> 2) & g31);
v = ((v + (v >> 3)) & g32) + ((v >> 6) & g32);
return (v + (v >> 9) + (v >> 18) + (v >> 27)) & 0x3f;
Re[2]: (C)
От: UgN  
Дата: 16.05.03 07:34
Оценка:
Здравствуйте, UgN, Вы писали:

По поводу авторства. Писал конечно не сам. Лень было набивать, если уже набито.

Видел приведенный выше текст на различных сайтах...

И многие скромно приписывали авторство себе любимому.

(Может так оно и есть. Идея-то несложная)

В связи с этим предлагаю считатаь, что (C) принадлежит народу.
Re: Подсчёт количества ненулевых бит в числе
От: ZORK Россия www.zorkaltsev.com
Дата: 16.05.03 07:39
Оценка: 4 (1)
Здравствуйте, DemAS, Вы писали:

DAS> Может кто-то захочет попробовать реализовать сам, а не срезу смотреть ответ.


http://www.rsdn.ru/Forum/Message.aspx?mid=258259#258259
Автор: ZORK
Дата: 04.05.03
Думать надо ...головой :)
Re[2]: Подсчёт количества ненулевых бит в числе
От: DemAS http://demas.me
Дата: 16.05.03 07:44
Оценка:
Этот пример я и видел. В связи с чем и возник этот вопрос — а как этот код работает ? Что за магичиские коэффициенты ?
Posted via RSDN NNTP Server 1.5 beta
Re: Подсчёт количества ненулевых бит в числе
От: rgl  
Дата: 16.05.03 07:45
Оценка:
Здравствуйте, DemAS, Вы писали:


DAS> Добрый день.


DAS> Интересует реализация такого алгоритма.


DAS> Честно говоря, одна из реализаций у меня есть, но я ее приведу в следующем сообщении. Может кто-то захочет попробовать реализовать сам, а не срезу смотреть ответ.



Можно считать биты не по одному, а по (например) 4

int nbits4[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };

........

while(......) {
  result += nbits4[ x & 0xF ];
  x >>= 4;
}
Re[3]: (C)
От: UgN  
Дата: 16.05.03 07:48
Оценка: 21 (1)
Здравствуйте, UgN, Вы писали:

Наверное, все-таки, автор -- Аркадий Белоусов

Есть ссылки на письмо от 19 сентября 1999 года в эхоконференции fido7.RU.ALGORITHMS.
Re: Подсчёт количества ненулевых бит в числе
От: Apapa Россия  
Дата: 16.05.03 08:18
Оценка: 29 (3)
Привет, DemAS!

DAS> Интересует реализация такого алгоритма.


Алгоритм, не привязанный к типу (я беру на примере числа с 32 разрядами):

mov ecx, 0
clc

shr eax, 1
@@loop:
    adc ecx, 0
    shr eax, 1
jnz @@loop
adc ecx, 0


Можно перед "@@loop:" убрать "shr eax, 1", но для большинства чисел получится лишнее сложение.

Вот примерчик (для Watcom — почему бы и нет?):

#include <iostream.h>

int count(int eax);
#pragma aux count parm nomemory [eax] = \
        "mov ecx, 0             "\
        "clc                    "\
        "shr eax, 1             "\
        "@@loop:                "\
        "    adc ecx, 0         "\
        "    shr eax, 1         "\
        "jnz @@loop             "\
        "adc ecx, 0             "\
        modify nomemory exact [eax ecx] \
        value [ecx];

void main(void) {
    int value;
    do {
        cin >> value;
        cout << count(value) << endl;
    } while(value);
};


Здесь могла бы быть Ваша реклама!
Re[3]: Подсчёт количества ненулевых бит в числе
От: UgN  
Дата: 16.05.03 08:24
Оценка: 39 (4)
Здравствуйте, DemAS, Вы писали:

DAS> Этот пример я и видел. В связи с чем и возник этот вопрос — а как этот код работает ? Что за магичиские коэффициенты ?


Давай для 8-бит

    v = (v & 0x55) + ((v >> 1) & 0x55);
    v = (v & 0x33) + ((v >> 2) & 0x33);
    return (v & 0x0f) + ((v >> 4) & 0x0f);



число -        abcd efgh = 01101011

Разбиваем на группы по 2 бита =>
Первая маска   0101 0101


Проверяем единицы в каждом четном бите (6-4-2-0)
|01|01| |01|01|
&
|ab|cd| |ef|gh|
 01 00   00 01
= 
b=1 d=0 f=0 h=1            (v & 0x55)
 
Сдвиг на пол-группы
Проверяем единицы в каждом нечетном бите (7-5-3-1) 
|01|01| |01|01|          
&
| a|bc| |de|fg|h
=
a=0 c=1 e=1 g=1            (v >> 1) & 0x55

После сложения получим количество единичных битов в каждой группе

Новое значение числа:
|a+b|c+d|e+f|g+h|
  1   1   1   2

|01|01| |01|10|            v = (v & 0x55) + ((v >> 1) & 0x55);


Теперь разбиваем на группы по 4 бита =>
Вторая маска   0011 0011

аналогично 

|0011| |0011|
&
|0101| |0110|
=
 0001   0010               (v & 0x33)

и со сдвигом на пол-группы

|0011| |0011|
&
|  01| |0101|10            (v >> 2) & 0x33
=
 0001   0001

После сложения получим количество единичных битов в каждой группе

 0001 0010
+
 0001 0001
=
 0010 0011                 v = (v & 0x33) + ((v >> 2) & 0x33);

(2 бита в первой иетраде и 3 во второй)

А всего значит 2+3 = 5 установленных битов

0010 + 0011 = 0101 // (v & 0x0f) + ((v >> 4) & 0x0f); 

И все!

Эти маски просто разбивает число на группы по сколько-то бит
Re[4]: Подсчёт количества ненулевых бит в числе
От: MichaelP  
Дата: 16.05.03 08:55
Оценка: 7 (1)
Здравствуйте, UgN, Вы писали:

UgN>Здравствуйте, DemAS, Вы писали:


DAS>> Этот пример я и видел. В связи с чем и возник этот вопрос — а как этот код работает ? Что за магичиские коэффициенты ?


UgN>Давай для 8-бит


UgN>
UgN>    v = (v & 0x55) + ((v >> 1) & 0x55);
UgN>    v = (v & 0x33) + ((v >> 2) & 0x33);
UgN>    return (v & 0x0f) + ((v >> 4) & 0x0f); 
UgN>


Маленькая оптимизация, которая, кстати, используется в решении для 32 бит.

Т.к. в последнем операторе суммарное количество бит не выходит за границу 4-битного числа, т.е. размера текущего блока. Маскировать в последнем операторе можно после выполнения операции:
return (v + (v >> 4)) & 0x0f;

Это же относится к большим размерам блока при большем чем 8 количестве бит в исходном числе.

Для обобщения следует отметить, что переходить к окончательному суммированию надо в тот момент, когда размера текущего блока хватает для сохранения окончательного результата.
Re[4]: (C)
От: MichaelP  
Дата: 19.05.03 12:35
Оценка: 14 (1)
Здравствуйте, UgN, Вы писали:

UgN>Здравствуйте, UgN, Вы писали:


UgN>Наверное, все-таки, автор -- Аркадий Белоусов


UgN>Есть ссылки на письмо от 19 сентября 1999 года в эхоконференции fido7.RU.ALGORITHMS.


В выходные книжку посмотрел, где, по моим смутным воспоминанием, была эта задача. Так вот:
Там идет отсылка на 1954 год, с примечанием, что автора указать трудно, но данный алгоритм использовался в одном из первых компьютеров.
Re: Подсчёт количества ненулевых бит в числе
От: Bleysus Россия  
Дата: 21.05.03 05:11
Оценка: 27 (1)
Здравствуйте, DemAS, Вы писали:


DAS> Добрый день.


DAS> Интересует реализация такого алгоритма.


DAS> Честно говоря, одна из реализаций у меня есть, но я ее приведу в следующем сообщении. Может кто-то захочет попробовать реализовать сам, а не срезу смотреть ответ.



Делишь на 2 для получения остатка от деления. Если остаток есть, тогда последний бит 1, если нет, то 0. Сдвигаешь на разряд вправо исходное число, последний бит убирается, и повторяешь деление. Только делить надо не результат деления а результат сдвига. Насколько я помню сдвиг вправо в асме делается командой shr, только не уверен, лет 6 с асмом не работал.
...:::In Hard We Trust:::...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.