Вес Хэмминга
Вес Хэмминга строки — количество символов, отличных от нулевого символа используемого алфавита. Таким образом, он эквивалентен расстоянию Хэмминга от состоящей только из нулей строки той же длины. В наиболее типичном случае, для заданного набора битов, вес равен количеству битов, установленных в 1, или сумме цифр двоичного представления заданного числа и ℓ₁ норме битового вектора[1].
| Строка | Вес Хэмминга |
|---|---|
| 11101 | 4 |
| 11101000 | 4 |
| 00000000 | 0 |
| 678012340567 | 10 |
История и использование
Вес Хэмминга назван именем американского математика Ричарда Уэсли Хэмминга, хотя он и не является автором этого понятия[3]. Вес Хэмминга двоичных чисел успользовался уже в 1899 году Джеймсом У. Ли Глейшером для формулы числа нечётных биномиальных коэффициентов[4] в одной строке треугольника Паскаля [5]. Ирвинг С. Рид ввёл понятие, эквивалентное весу Хэмминга для бинарного случая, в 1954 году[6].
Вес Хэмминга используется в некоторых дисциплинах, включая теорию информации, теорию кодирования и криптографию. Примеры приложений веса Хэмминга включают:
- В алгоритме модульного быстрого возведения в степень, число модульных умножений, требуемых для степени n, равно log2 n + вес(n). Именно поэтому в качестве публичного ключа n, используемого в RSA, обычно выбирается число с низким весом Хэмминга[7].
- Вес Хэмминга определяет длины путей между узлами в распределённых хеш-таблицах Chord[8].
- Поиск радужной оболочки глаза в биометрических базах данных обычно реализуется путём вычисления расстояния Хэмминга для каждой записи[9].
- В программах компьютерных шахмат, использующих представление битовой карты, вес Хэмминга битовой карты указывает количество оставшихся фигур определённого типа или количество клеток доски, контролируемых фигурами одного игрока, и поэтому является важным фактором, влияющим на оценку позиции[10].
- Вес Хэмминга может быть использован для эффективного нахождения первого установленного бита используя равенство ffs(x) = pop(x ^ (x - 1)). Это полезно на платформах, таких как SPARC, которые имеют аппаратные инструкции для подсчёта веса Хэмминга, но не имеют аппаратной инструкции для поиска первого установленного бита[11][12].
- Операция вычисления веса Хэмминга может быть интерпретирована как преобразование из унарной системы в двоичное число[13].
Эффективная реализация
Подсчёт единиц в битовоё строке часто требуется в криптографии и других приложениях. Расстояние Хэмминга между двумя словами A и B можно вычислить как вес Хэмминга A XOR B[12].
Вопрос эффективной реализации этой задачи широко изучался. Некоторые процессоры предоставляют специальные инструкции для такого вычисления или параллельные операции над битовыми векторами. Для процессоров, не обладающих такими возможностями, наилучшие известные решения основаны на суммировании счётчиков по схеме дерева. Например, чтобы подсчитать количество единичных битов в 16-битном двоичном числе a = 0110 1100 1011 1010, можно выполнить следующие операции:
| Выражение | Двоичные | Десятичные | Комментарии | |||||||
|---|---|---|---|---|---|---|---|---|---|---|
a
|
01 | 10 | 11 | 00 | 10 | 11 | 10 | 10 | 27834 | Исходное число |
b0 = (a >> 0) & 01 01 01 01 01 01 01 01
|
01 | 00 | 01 | 00 | 00 | 01 | 00 | 00 | 1, 0, 1, 0, 0, 1, 0, 0 | Каждый второй (нечётный) бит числа a |
b1 = (a >> 1) & 01 01 01 01 01 01 01 01
|
00 | 01 | 01 | 00 | 01 | 01 | 01 | 01 | 0, 1, 1, 0, 1, 1, 1, 1 | оставшиеся биты числа a |
c = b0 + b1
|
01 | 01 | 10 | 00 | 01 | 10 | 01 | 01 | 1, 1, 2, 0, 1, 2, 1, 1 | Число единиц в каждой паре битов числа a |
d0 = (c >> 0) & 0011 0011 0011 0011
|
0001 | 0000 | 0010 | 0001 | 1, 0, 2, 1 | Каждый второй счётчик числа c | ||||
d2 = (c >> 2) & 0011 0011 0011 0011
|
0001 | 0010 | 0001 | 0001 | 1, 2, 1, 1 | Оставшиеся счётчики числа c | ||||
e = d0 + d2
|
0010 | 0010 | 0011 | 0010 | 2, 2, 3, 2 | Счётчики единиц в каждой 4-битной группе числа a | ||||
f0 = (e >> 0) & 00001111 00001111
|
00000010 | 00000010 | 2, 2 | Каждый второй счётчик числа e | ||||||
f4 = (e >> 4) & 00001111 00001111
|
00000010 | 00000011 | 2, 3 | Остальные счётчики числа e | ||||||
g = f0 + f4
|
00000100 | 00000101 | 4, 5 | Счётчики единиц в каждой 8-битной группе числа a | ||||||
h0 = (g >> 0) & 0000000011111111
|
0000000000000101 | 5 | Каждый второй счётчик числа g | |||||||
h8 = (g >> 8) & 0000000011111111
|
0000000000000100 | 4 | Остальные счётчики числа g | |||||||
i = h0 + h8
|
0000000000001001 | 9 | Число единиц всего 16-битного слова | |||||||
Здесь операции указаны в семантике языка C, так что X >> Y означает сдвиг X вправо на Y бит, X & Y означает битовое AND чисел X и Y, а + является обычной операцией сложения. Лучшие известные алгоритмы для этой проблемы основываются на концепции, проиллюстрированной выше и приведены здесь[12]:
//типы и константы, используемые в функциях ниже
//uint64_t — беззнаковая 64-битная целая переменная (определена в C99 версии языка C)
const uint64_t m1 = 0x5555555555555555; //двоичное: 0101...
const uint64_t m2 = 0x3333333333333333; // двоичное: 00110011..
const uint64_t m4 = 0x0f0f0f0f0f0f0f0f; // двоичное: 4 нуля, 4 единицы ...
const uint64_t m8 = 0x00ff00ff00ff00ff; // двоичное: 8 нуля, 8 единиц...
const uint64_t m16 = 0x0000ffff0000ffff; // двоичное: 16 нуля, 16 единиц...
const uint64_t m32 = 0x00000000ffffffff; // двоичное: 32 нуля, 32 единицы
const uint64_t h01 = 0x0101010101010101; //сумма 256 в степени 0,1,2,3...
//Это примитивная реализация, представленная для сравнения,
//чтобы помочь понять более совершенные функции
//Этот алгоритм использует 24 арифметических операции (сдвиг, прибавить, и).
int popcount64a(uint64_t x)
{
x = (x & m1 ) + ((x >> 1) & m1 ); // поместить счётчик каждых 2 бит в эти 2 бита
x = (x & m2 ) + ((x >> 2) & m2 ); // поместить счётчик каждых 4 бит в эти 4 бита
x = (x & m4 ) + ((x >> 4) & m4 ); // поместить счётчик каждых 8 бит в эти 8 бит
x = (x & m8 ) + ((x >> 8) & m8 ); // поместить счётчик каждых 16 бит в эти 16 бит
x = (x & m16) + ((x >> 16) & m16); // поместить счётчик каждых 32 бит в эти 32 бит
x = (x & m32) + ((x >> 32) & m32); // поместить счётчик каждых 64 бит в эти 64 бита
return x;
}
// Эта реализация использует меньше арифметических операций, чем известные
// другие реализации на машинах с медленным умножением.
// Этот алгоритм использует 17 арифметических операций.
int popcount64b(uint64_t x)
{
x -= (x >> 1) & m1; // поместить счётчик каждых 2 бит в эти 2 бита
x = (x & m2) + ((x >> 2) & m2); // поместить счётчик каждых 4 бит в эти 4 бита
x = (x + (x >> 4)) & m4; // поместить счётчик каждых 8 бит в эти 8 бит
x += x >> 8; // поместить счётчик каждых 16 бит в его младшие 8 бит.
x += x >> 16; // поместить счётчик каждых 32 бит в его младшие 8 бит
x += x >> 32; // поместить счётчик каждых 64 бит в его младшие 8 бит
return x & 0x7f;
}
// Эта реализация использует меньше арифметических операций, чем известные
// другие реализации на машинах с быстрым умножением.
// Этот алгоритм использует 12 арифметических операций, одна из которых — умножение.
int popcount64c(uint64_t x)
{
x -= (x >> 1) & m1; // поместить счётчик каждых 2 бит в эти 2 бита
x = (x & m2) + ((x >> 2) & m2); // поместить счётчик каждых 4 бит в эти 4 бита
x = (x + (x >> 4)) & m4; // поместить счётчик каждых 8 бит в эти 8 бит
return (x * h01) >> 56; //вернуть левые 8 бит значения x + (x<<8) + (x<<16) + (x<<24) + ...
}
Вышеприведённые реализации демонстрируют наилучшее поведение в худшем случае среди всех известных алгоритмов. Как писал Вегнер в 1960 году[14], побитовое AND x с x − 1 отличается от x только обнулением наименее значимого разряда. Вычитание 1 преобразует самую правовую последовательность нулей в единицы, а самую правую единицу — в нуль. Если x изначально имел n единичных бит, то после всего n итераций этой операции x превратится в нуль. Следующая реализация основана на этом принципе.
// Эта реализация лучше, когда большинство бит равны нулю
// Алгоритм работает одинаково для всех размеров данных.
//Этот алгоритм использует 3 арифметических операции и одно сравнение/переход на "1" бит в x.
int popcount64d(uint64_t x)
{
int count;
for (count=0; x; count++)
x &= x - 1;
return count;
}
Здесь представляет интерес тесная связь между подсчётом числа единиц нахождением первой единицы (FFS ) и число старших нулевых битов подряд (CLZ).
Если разрешено использование большее количество памяти, мы можем вычислить вес Хэмминга быстрее, чем описано выше. При неограниченной памяти мы можем просто создать большую таблицу весов Хэмминга для каждого 64-битного целого. Если мы можем запомнить таблицу функции Хэмминга каждого 16-битного целого, мы можем сделать следующее для вычисления веса Хэмминга любого 32-битного целого:
static uint8_t wordbits[65536] = { /* число единиц в целых от 0 до 65535 включительно */ };
// Этот алгоритм использует 3 арифметические операции и 2 чтения из памяти.
int popcount32e(uint32_t x)
{
return wordbits[x & 0xFFFF] + wordbits[x >> 16];
}
// Таблицу wordbits[] можно заполнить, использовав следующую функцию
int popcount32e_init(void)
{
uint32_t i;
uint16_t x;
int count;
for (i=0; i <= 0xFFFF; i++)
{
x = i;
for (count=0; x; count++) // заимствовано из popcount64d() выше
x &= x - 1;
wordbits[i] = count;
}
}
Рекурсивный алгоритм дан в книге Донована и Кернигана[15]
/* Вес i может отличаться от веса i / 2 только в младшем разряде i */
int popcount32e_init (void)
{
int i;
for (i = 1; sizeof wordbits / sizeof *wordbits > i; ++i)
wordbits [i] = wordbits [i >> 1] + (1 & i);
}
Мула и др.[16] показали, что векторизованная версия popcount64b может работать быстрее, чем специализированные инструкции (например, popcnt на процессорах x64).
Алгоритм Харли-Сила[17] является одним из самых быстрых среди алгоритмов, использующих только операции над целыми числами[18].
Минимальный вес
В корректирующем коде минимальный вес Хэмминга, обычно называемый минимальным весом wmin кода — это наименьший вес кода ненулевого слова. Вес w слова — это число единиц в слове. Например, слово 11001010 имеет вес 4.
В линейном блочном коде минимальный вес является также минимальным расстоянием Хэмминга (dmin) и определяет способность коррекции ошибки кода. Если , и код будет корректным до ошибок[19].
Языковая поддержка
Некоторые компиляторы языка C предлагают встроенные функции для подсчёта битов. Например, GCC (с версии 3.4 апреля 2004 года) содержит встроенную функцию
__builtin_popcount, которая при наличии соответствующей инструкции процессора использует её, а в противном случае — эффективную библиотечную реализацию[20]. Компилятор LLVM-GCC поддерживает эту функцию с версии 1.5 июня 2005 года[21].
В стандартной библиотеке C++ структура данных bitset имеет метод count(), который считает число установленных бит. В C++20 был добавлен новый заголовочный файл <bit>, содержащий функции std::popcount и std::has_single_bit, принимающие аргументы беззнаковых целочисленных типов.
В Java структура данных BitSet, расширяемый битовый массив, имеет метод BitSet.cardinality(), подсчитывающий количество установленных битов. Кроме того, существуют функции Integer.bitCount(int) и Long.bitCount(long) для подсчёта числа бит в 32-битных и 64-битных целых соответственно. Также класс BigInteger, работающий с целыми числами произвольной точности, имеет метод BigInteger.bitCount(), который подсчитывает биты.
В Python тип int имеет метод bit_count(), который подсчитывает количество установленных битов. Эта функция появилась в Python 3.10, выпущенном в октябре 2021 года[22].
В Common Lisp функция logcount для неотрицательного целого числа возвращает количество единичных битов. (Для отрицательных чисел она возвращает количество нулевых битов в представлении дополнительного кода.) В обоих случаях целое число может быть BIGNUM.
Начиная с версии GHC 7.4 базовый пакет Haskell содержит функцию popCount, доступную для всех типов, реализующих класс Bits (из модуля Data.Bits)[23].
Версия языка SQL в базе данных MySQL обеспечивает встроенную стандартную функцию BIT_COUNT()[24].
Фортран 2008 имеют стандартную встроенную функцию popcnt, возвращающую количество ненулевых битов в целом числе (или массиве целых чисел)[25].
Некоторые программируемые научные карманные калькуляторы имеют специальные команды для подсчёта установленных битов, например, #B на HP-16C[26].
Free Pascal реализует popcnt начиная с версии 3.0[27].
Поддержка на процессорах
- Компьютер IBM 7030 Stretch в 1960-х годах вычислял число установленных битов, как и число ведущих нулей как побочный продукт всех логических операций[12].
- Ранние суперкомпьютеры Cray имели инструкцию подсчёта количества установленных битов, которая, по слухам, была специально запрошена Агентство национальной безопасности США для приложений криптоанализа[12].
- В машинах серий 6000 и Cyber 70/170 компании Control Data Corporation также присутствовала инструкция подсчёта битов. В ассемблере COMPASS она кодировалась как
CXi. - 64-битная архитектура SPARC версии 9 определяет инструкцию
POPC[11][12], однако большинство реализаций её не поддерживают, и она эмулируется операционной системой[28]. - Модель компьютера MMIX, разработанная Дональдом Кнутом для замены MIX в его книге Искусство программирования с 1999 года имеет инструкцию
SADD. ИнструкцияSADD a,b,cподсчитывает все единичные биты в b, которые одновременно являются нулевыми в c и записывает результат в a. - Процессор Compaq Alpha 21264, выпущенный в 1999 году, стал первым в серии Alpha, в котором была реализована инструкция подсчёта (
CIX). - Процессоры Blackfin от Analog Devices оснащены инструкцией
ONESдля выполнения 32-битного подсчёта установленных битов[29]. - Архитектура Barcelona от AMD представила набор инструкций для продвинутой битовой манипуляции, включающий инструкцию
POPCNTв составе расширений SSE4a в 2007 году. - Процессоры Intel Core получили инструкцию
POPCNTв составе расширения SSE4.2 системы команд, впервые появившегося в процессоре Core i7 на базе Nehalem, выпущенном в ноябре 2008 года. - Архитектура ARM представила инструкцию
VCNTкак часть расширений Advanced SIMD (NEON). - Архитектура RISC-V добавила инструкцию
CPOPкак часть расширения Bit Manipulation (B)[30].
См. также
Примечания
- ↑ В английском языке вес Хэмминга в бинарном случае называется также population count, popcount, sideways sum или bit summation.
- ↑ R.Ugalde, Laurence. Population count the Fōrmulæ programming language. Fōrmulæ. Дата обращения: 2 июня 2024.
- ↑ Thompson, 1983, с. 33.
- ↑ Последовательность Гулда (15 января 2022). Дата обращения: 18 декабря 2025.
- ↑ Glaisher, 1899, с. 150–156.
- ↑ Reed, 1954, с. 38–49.
- ↑ Cohen, Lobstein, Naccache, Zémor, 1998, с. 211–220.
- ↑ Stoica, Morris etc, 2003, с. 17–32.
- ↑ Kong, Zhang, Kamel, 2010, с. 522–532.
- ↑ Heinz, 1997, с. 166–176.
- ↑ 1 2 SPARC, 1992, с. 205.
- ↑ 1 2 3 4 5 6 Warren Jr., 2013, с. 81–96.
- ↑ Blaxell, 1978, с. 146–156.
- ↑ Wegner, 1960, с. 322.
- ↑ Donovan, Kernighan, 2016.
- ↑ Muła, Kurz, Lemire, 2018.
- ↑ Sse-popcount/Popcnt-harley-seal.CPP at master · WojciechMula/Sse-popcount. GitHub.
- ↑ Muła, Kurz, Lemire, 2018, с. 111–120.
- ↑ Stern, Mahmoud, 2004, с. 477.
- ↑ GCC 3.4 Release Notes. GNU Project.
- ↑ LLVM 1.5 Release Notes. LLVM Project.
- ↑ What's New In Python 3.10. python.org.
- ↑ GHC 7.4.1 release notes. GHC documentation.
- ↑ Chapter 12.11. Bit Functions — MySQL 5.0 Reference Manual.
- ↑ Metcalf, Reid, Cohen, 2011, с. 380.
- ↑ HP-16C, 1982.
- ↑ Free Pascal documentation popcnt. Дата обращения: 7 декабря 2019.
- ↑ JDK-6378821: bitCount() should use POPC on SPARC processors and AMD+10h. Java bug database (30 января 2006).
- ↑ Blackfin, 2001, с. 8–24.
- ↑ Wolf, Claire. RISC-V "B" Bit Manipulation Extension for RISC-V, Draft v0.37. Github (22 марта 2019).
Литература
- Kong A.W.K., Zhang D., Kamel M.S. An analysis of IrisCode // IEEE Transactions on Image Processing. — 2010. — Февраль (т. 19, вып. 2). — doi:10.1109/tip.2009.2033427. — Bibcode:2010ITIP...19..522K. — PMID 20083454.
- Heinz E.A. How Darkthought plays chess // ICGA Journal. — 1997. — Сентябрь (т. 20, вып. 3). — doi:10.3233/icg-1997-20304.
- Обновлено и перепечатано в книге Scalable Search in Computer Chess. — Vieweg+Teubner Verlag, 2000. — С. 185–198. — doi:10.1007/978-3-322-90178-1_13.
- SPARC International, Inc. A.41: Population Count. Programming Note // The SPARC architecture manual: version 9. — Version 9. — Englewood Cliffs, New Jersey, USA: Prentice Hall, 1992. — ISBN 0-13-825001-4.
- Donald Ervin Knuth. Bitwise tricks & techniques; Binary Decision Diagrams // The Art of Computer Programming. — Addison–Wesley Professional, 2009. — Т. 4, fascicle 1. — ISBN 978-0-321-58050-4. (NB. Draft of Fascicle 1b Архивировано 12 марта 2016 года. доступна для загрузки.)
- Henry S. Warren Jr. Hacker's Delight. — 2. — Addison Wesley - Pearson Education, Inc., 2013. — ISBN 978-0-321-84268-8.
- David Blaxell. Record linkage by bit pattern matching // Computer Science and Statistics--Tenth Annual Symposium on the Interface / (ed) Hogben D., Fife D. W.. — U.S. Department of Commerce / National Bureau of Standards, 1978. — Т. 503.
- Thomas M. Thompson. From Error-Correcting Codes through Sphere Packings to Simple Groups. — The Mathematical Association of America, 1983. — (The Carus Mathematical Monographs #21).
- Michael Metcalf, John Reid, Malcolm Cohen. Modern Fortran Explained. — Oxford University Press, 2011. — ISBN 978-0-19-960142-4.
- Hewlett-Packard HP-16C Computer Scientist Owner's Handbook. — Hewlett-Packard Company, 1982. — [Архивировано 28 марта 2017 года.]
- Blackfin Instruction Set Reference. — Preliminary. — Analog Devices, 2001.
- Alan Donovan, Brian Kernighan. The Go Programming Language. — Addison-Weseley, 2016. — ISBN 978-0-13-419044-0.
- Gérard Denis Cohen, Antoine Lobstein, David Naccache, Gilles Zémor. How to improve an exponentiation black-box // Advances in Cryptology – EUROCRYPT '98, International Conference on the Theory and Application of Cryptographic Techniques, Espoo, Finland, May 31 – June 4, 1998, Proceeding / (ed) Kaisa Nyberg. — Springer, 1998. — Т. 1403. — (Lecture Notes in Computer Science). — ISBN 978-3-540-64518-4. — doi:10.1007/BFb0054128.
- Stoica I., Morris R., Liben-Nowell D., Karger D. R., Kaashoek M. F., Dabek F., Balakrishnan H. Chord: a scalable peer-to-peer lookup protocol for internet applications // IEEE/ACM Transactions on Networking. — 2003. — Февраль (т. 11, вып. 1). — doi:10.1109/TNET.2002.808407. — Bibcode:2003ITNet..11...17S.
Цитата (Секция 6.3): «In general, the number of fingers we need to follow will be the number of ones in the binary representation of the distance from node to query.» - A technique for counting ones in a binary computer // Communications of the ACM. — 1960. — Май (т. 3, вып. 5). — doi:10.1145/367236.367286.
- Wojciech Muła, Nathan Kurz, Daniel Lemire. Faster Population Counts Using AVX2 Instructions // Computer Journal. — 2018. — Январь (т. 61, вып. 1). — doi:10.1093/comjnl/bxx046. — arXiv:1611.07612.
- James Whitbread Lee Glaisher. On the residue of a binomial-theorem coefficient with respect to a prime modulus // The Quarterly Journal of Pure and Applied Mathematics. — 1899. — Т. 30. (NB. См., в частности последний параграф на стр. 156.)
- Irving Stoy Reed. A Class of Multiple-Error-Correcting Codes and the Decoding Scheme // IRE Professional Group on Information Theory. — Institute of Radio Engineers (IRE), 1954. — Т. PGIT-4.
- Wojciech Muła, Nathan Kurz, Daniel Lemire. Faster Population Counts Using AVX2 Instructions // The Computer Journal. — 2018. — Т. 61. — P. 111–120. — doi:10.1093/comjnl/bxx046. — arXiv:1611.07612.
- Stern, Mahmoud. Communications System Design. — Prentice Hall,, 2004.
Литература для дальнейшего чтения
- Michael Beeler, Ralph William Gosper, Richard C. Schroeppel. compilation // HAKMEM. — Artificial Intelligence Laboratory, Massachusetts Institute of Technology, Cambridge, Massachusetts, USA, 1972. (Item 169: Population count assembly code for the PDP/6-10.)
Ссылки
В английском языке вес Хэмминга в бинарном случае называется также population count[1], popcount, sideways sum[2] или bit summation[3].
- Aggregate Magic Algorithms. Optimized population count and other algorithms explained with sample code.
- Bit Twiddling Hacks Several algorithms with code for counting bits set.
- Necessary and Sufficient Архивировано 23 сентября 2017 года. - by Damien Wintour - Has code in C# for various Вес Хэмминга implementations.
- Best algorithm to count the number of set bits in a 32-bit integer? - Stackoverflow
- ↑ Warren Jr., 2013, с. 81–96.
- ↑ Knuth, 2009.
- ↑ HP-16C, 1982.