Бит-массив в С++

При работе с проблемами Project Euler мне часто нужны большие ( > 10 ** 7) битовые массивы.

Мой обычный подход является одним из:

bool* sieve = new bool[N];

bool sieve[N];

Когда N = 1,000,000, моя программа использует 1 мегабайт (8 * 1 000 000 бит).

Существует ли более эффективный способ использования массивов бит хранилища, чем bool в С++?

Ответы

Ответ 1

Используйте std::bitset (если N - константа), в противном случае используйте std::vector<bool>, как упомянули другие (но не забывайте прочтение эта отличная статья от Herb Sutter)

Битовый набор - это специальный класс контейнера, который предназначен для хранения бит (элементы с двумя возможными значениями: 0 или 1, true или false,...).

Класс очень похож на обычный массив , но оптимизирует для размещения пространства: каждый элемент занимает только один бит (что в восемь раз меньше наименьшего элементального типа в С++: char).

ИЗМЕНИТЬ

Herb Sutter (в этой статье) упоминает, что

Причина std::vector <bool> является несоответствующим, так это то, что он тянет трюки под обложками в попытке оптимизировать пространство: вместо хранения полного char или int для каждого bool [1] (занимая по крайней мере 8 раз пространство на платформах с 8 -битные символы), он упаковывает bools и сохраняет их как отдельные биты (внутри, скажем, символы) во внутреннем представлении.

std::vector <bool> создает определенную оптимизацию для всех пользователей, закрепляя ее в стандарте. Это не очень хорошая идея; разные пользователи имеют разные требования, и теперь все пользователи вектора должны платить штраф за производительность, даже если они не хотят или нуждаются в экономии пространства.

РЕДАКТИРОВАТЬ 2:

И если вы использовали Boost, вы можете использовать boost::dynamic_bitset (если N известно во время выполнения)

Ответ 2

Для лучшего или худшего, std::vector<bool> будет использовать биты вместо bool's, чтобы сэкономить место. Поэтому просто используйте std::vector, как вы должны были быть в первую очередь.

Если N - константа, вы можете использовать std::bitset.

Ответ 3

Вы можете посмотреть std::bitset и std::vector<bool>. Последнее часто рекомендуется против, потому что, несмотря на vector в названии, он действительно не действует как вектор любого другого типа объекта и на самом деле не отвечает требованиям для контейнера в целом. Тем не менее, это может быть очень полезно.

OTOH, ничто не собирается (по крайней мере надежно) хранить 1 миллион значений bool менее чем за 1 миллион бит. Это просто невозможно сделать с какой-либо определенностью. Если ваши битовые наборы содержат степень избыточности, существуют различные схемы сжатия, которые могут быть эффективными (например, LZ *, Хаффман, арифметика), но без какого-либо знания содержимого невозможно сказать, что они были бы наверняка. Тем не менее, каждый из них, как правило, сохраняет каждый бит/бит только в одном бите памяти (плюс немного накладных расходов на бухгалтерию), но обычно это константа и порядка байтов до десятков байтов не более).

Ответ 4

Тип "bool" не сохраняется с использованием только 1 бит. Из вашего комментария о размере, похоже, для каждого bool используется 1 полный байт.

A C подобным образом:

uint8_t sieve[N/8]; //array of N/8 bytes

а затем логические ИЛИ байты вместе, чтобы получить все ваши биты:

sieve[0] = 0x01 | 0x02; //this would turn on the first two bits

В этом примере 0x01 и 0x02 представляют собой шестнадцатеричные числа, представляющие байты.

Ответ 5

Да, вы можете использовать bitset.

Ответ 6

Вам может быть интересно попробовать библиотеку BITSCAN в качестве альтернативы. Недавно было предложено расширение для разреженности, которое я не уверен в вашем случае, но может быть.

Ответ 8

Вы можете использовать массив байтов и индекс. Индекс n будет в байтовом индексе n/8, бит # n%8. (В случае, если std:: bitset недоступен по какой-либо причине).

Ответ 10

Тип "bool" не сохраняется с использованием только 1 бит. Из вашего комментария о размере, кажется, для каждого bool используется 1 полный байт.

A C подобным образом:

uint8_t sieve[N/8]; //array of N/8 bytes

Элемент массива:

result = sieve[index / 8] || (1 << (index % 8)); 

или

result = sieve[index >> 3] || (1 << (index & 7));

установить 1 в массив:

sieve[index >> 3] |= 1 << (index & 7);