Мне нужен быстрый способ подсчета количества бит в целом числе в Python. Мое текущее решение
bin(n).count("1")
но мне интересно, есть ли более быстрый способ сделать это?
PS: (я представляю большой двумерный двоичный массив как единый список чисел и выполняю побитовые операции, и это сокращает время с часов до минут. И теперь я хотел бы избавиться от этих лишних минут.
Изменить: 1. он должен быть в Python 2.7 или 2.6
и оптимизация для небольших чисел не имеет большого значения, поскольку это не будет явным узким местом, но у меня есть числа с более чем 10000 битами в некоторых местах
например, это 2000-битный случай:
12448057941136394342297748548545082997815840357634948550739612798732309975923280685245876950055614362283769710705811182976142803324242407017104841062064840113262840137625582646683068904149296501029754654149991842951570880471230098259905004533869130509989042199261339990315125973721454059973605358766253998615919997174542922163484086066438120268185904663422979603026066685824578356173882166747093246377302371176167843247359636030248569148734824287739046916641832890744168385253915508446422276378715722482359321205673933317512861336054835392844676749610712462818600179225635467147870208L
int
? Разве у этого нет собственного метода расчета этого?int.bit_length
должен быть ответ, а не тот, который принят ниже.Ответы:
Для целых чисел произвольной длины
bin(n).count("1")
это самое быстрое, что я мог найти в чистом Python.Я попытался адаптировать решения Оскара и Адама для обработки целого числа в 64-битных и 32-битных частях соответственно. Оба были как минимум в десять раз медленнее
bin(n).count("1")
(32-битная версия заняла примерно вдвое меньше времени).С другой стороны, gmpy
popcount()
занял около 20% времениbin(n).count("1")
. Итак, если вы можете установить gmpy, используйте его.Чтобы ответить на вопрос в комментариях, для байтов я бы использовал таблицу поиска. Вы можете сгенерировать его во время выполнения:
Или просто определите это буквально:
Затем нужно
counts[x]
получить количество 1 бит,x
где 0 ≤ x ≤ 255.источник
bin(n).count("0")
неточно из-за префикса «0b».bin(n)[2:].count('0')
Должен быть для тех, кто считает непослушных ....numpy
массива.bin(n).count("1")
. Однако это превосходит только 60% представления Python. @ leetcodeВы можете адаптировать следующий алгоритм:
Это работает для 64-битных положительных чисел, но его легко расширить, и количество операций растет с логарифмом аргумента (т.е. линейно с размером бит аргумента).
Чтобы понять, как это работает, представьте, что вы разделяете всю 64-битную строку на 64 1-битных сегмента. Значение каждой корзины равно количеству битов, установленных в корзине (0, если биты не установлены, и 1, если установлен один бит). Первое преобразование приводит к аналогичному состоянию, но с 32 блоками длиной 2 бита каждая. Это достигается за счет соответствующего сдвига сегментов и добавления их значений (одно добавление заботится обо всех сегментах, поскольку перенос между сегментами не может происходить - n-битное число всегда достаточно велико для кодирования числа n). Дальнейшие преобразования приводят к состояниям с экспоненциально уменьшающимся числом сегментов экспоненциально растущего размера, пока мы не дойдем до одного 64-битного сегмента. Это дает количество битов, установленных в исходном аргументе.
источник
CountBits()
для обработки 10-битных чисел, добавив всего 8 строк кода. Но он станет громоздким из-за огромных констант.numpy
массивами.Вот реализация Python алгоритма подсчета населения, как описано в этом посте :
Это будет работать
0 <= i < 0x100000000
.источник
bin(n).count("1")
.%timeit numberOfSetBits(23544235423)
:1000000 loops, best of 3: 818 ns per loop
;%timeit bitCountStr(23544235423)
:1000000 loops, best of 3: 577 ns per loop
.numberOfSetBits
обрабатывает мой 864 × 64numpy.ndarray
за 841 мкс. СbitCountStr
я должен явно зацикливаться, и это занимает 40,7 мс, или почти в 50 раз больше.Согласно этому сообщению , это одна из самых быстрых реализаций веса Хэмминга (если вы не против использования около 64 КБ памяти).
В Python 2.x вам следует заменить
range
наxrange
.редактировать
Если вам нужна лучшая производительность (а ваши числа - большие целые числа), загляните в
GMP
библиотеку. Он содержит написанные от руки реализации сборки для многих различных архитектур.gmpy
- это модуль расширения Python с кодом C, который является оболочкой для библиотеки GMP.источник
array.array
forPOPCOUNT_TABLE16
, поскольку тогда он будет храниться как массив целых чисел, а не как списокint
объектов Python с динамическим размером .Мне очень нравится этот метод. Это просто и довольно быстро, но также не ограничено длиной в битах, поскольку Python имеет бесконечные целые числа.
На самом деле это более хитро, чем кажется, потому что позволяет не тратить время на сканирование нулей. Например, для подсчета установленных битов в 1000000000000000000000010100000001 потребуется то же время, что и в 1111.
источник
bin(n).count("1")
но для вашей функции потребовалось 3,8 с. Если бы в числах было установлено очень мало битов, он работал бы быстро, но если вы возьмете любое случайное число, в среднем вышеуказанная функция будет работать на порядки медленнее.Вы можете использовать алгоритм, чтобы получить двоичную строку [1] целого числа, но вместо конкатенации строки, подсчитывая количество единиц:
[1] https://wiki.python.org/moin/BitManipulation
источник
Вы сказали, что Нампи был слишком медленным. Вы использовали его для хранения отдельных битов? Почему бы не расширить идею использования целых чисел как битовых массивов, но использовать Numpy для их хранения?
Храните n битов как массив
ceil(n/32.)
32-битных целых чисел. Затем вы можете работать с массивом numpy таким же (ну, достаточно похожим) способом, которым вы используете int, в том числе используя их для индексации другого массива.Алгоритм заключается в параллельном вычислении количества битов, установленных в каждой ячейке, и их суммировании количества битов каждой ячейки.
Хотя я удивлен, что никто не предложил вам написать модуль C.
источник
источник
Оказывается, ваше начальное представление - это список списков целых чисел, равных 1 или 0. Просто посчитайте их в этом представлении.
Количество бит в целом числе в Python постоянно.
Однако, если вы хотите подсчитать количество установленных битов, самый быстрый способ - создать список, соответствующий следующему псевдокоду:
[numberofsetbits(n) for n in range(MAXINT)]
Это обеспечит вам постоянный поиск по времени после создания списка. См. Ответ @ PaoloMoretti для хорошей реализации этого. Конечно, вам не обязательно хранить все это в памяти - вы можете использовать какое-то постоянное хранилище значений ключей или даже MySql. (Другой вариант - реализовать собственное простое дисковое хранилище).
источник