Как обнулить вектор <bool>?
У меня есть vector<bool>
, и я бы хотел его обнулить. Мне нужен размер, чтобы оставаться тем же.
Обычный подход - это перебрать все элементы и reset их. Однако vector<bool>
представляет собой специально оптимизированный контейнер, который, в зависимости от реализации, может хранить только один бит на элемент. Есть ли способ воспользоваться этим, чтобы эффективно очистить все это?
bitset
, вариант с фиксированной длиной, имеет функцию set
. Имеет ли vector<bool>
нечто подобное?
Ответы
Ответ 1
Вам не повезло. std::vector<bool>
- это специализация, которая, по-видимому, даже не гарантирует непрерывную память или итераторы произвольного доступа (или даже переадресация?!), по крайней мере, на основе моего чтения cppreference - декодирование стандарта станет следующим шагом.
Так напишите конкретный код реализации, помолитесь и используйте стандартную методику обнуления или не используйте тип. Я голосую 3.
Полученная мудрость заключается в том, что она была ошибкой и может стать устаревшей. Если возможно, используйте другой контейнер. И определенно не возитесь с внутренними кишками, или полагайтесь на свою упаковку. Проверьте, есть ли у вас динамический бит в вашей библиотеке std
mayhap, или сверните свою собственную оболочку вокруг std::vector<unsigned char>
.
Ответ 2
В ответах, которые были опубликованы до сих пор, кажется, есть много догадок, но очень мало фактов, поэтому, возможно, было бы целесообразно провести небольшое тестирование.
#include <vector>
#include <iostream>
#include <time.h>
int seed(std::vector<bool> &b) {
srand(1);
for (int i = 0; i < b.size(); i++)
b[i] = ((rand() & 1) != 0);
int count = 0;
for (int i = 0; i < b.size(); i++)
if (b[i])
++count;
return count;
}
int main() {
std::vector<bool> bools(1024 * 1024 * 32);
int count1= seed(bools);
clock_t start = clock();
bools.assign(bools.size(), false);
double using_assign = double(clock() - start) / CLOCKS_PER_SEC;
int count2 = seed(bools);
start = clock();
for (int i = 0; i < bools.size(); i++)
bools[i] = false;
double using_loop = double(clock() - start) / CLOCKS_PER_SEC;
int count3 = seed(bools);
start = clock();
size_t size = bools.size();
bools.clear();
bools.resize(size);
double using_clear = double(clock() - start) / CLOCKS_PER_SEC;
int count4 = seed(bools);
start = clock();
std::fill(bools.begin(), bools.end(), false);
double using_fill = double(clock() - start) / CLOCKS_PER_SEC;
std::cout << "Time using assign: " << using_assign << "\n";
std::cout << "Time using loop: " << using_loop << "\n";
std::cout << "Time using clear: " << using_clear << "\n";
std::cout << "Time using fill: " << using_fill << "\n";
std::cout << "Ignore: " << count1 << "\t" << count2 << "\t" << count3 << "\t" << count4 << "\n";
}
Итак, это создает вектор, устанавливает в него некоторые случайно выбранные биты, подсчитывает их и очищает (и повторяет). Настройка/подсчет/печать выполняется для обеспечения того, что даже при агрессивной оптимизации компилятор не может/не будет оптимизировать наш код, чтобы очистить вектор.
Я нашел результаты интересными, мягко говоря. Сначала результат с VС++:
Time using assign: 0.141
Time using loop: 0.068
Time using clear: 0.141
Time using fill: 0.087
Ignore: 16777216 16777216 16777216 16777216
Таким образом, с помощью VС++ самый быстрый метод - это то, что вы, вероятно, изначально считаете самым наивным - цикл, который присваивает каждому отдельному элементу. С g++ результаты только немного отличаются, хотя:
Time using assign: 0.002
Time using loop: 0.08
Time using clear: 0.002
Time using fill: 0.001
Ignore: 16777216 16777216 16777216 16777216
Здесь цикл (безусловно) самый медленный метод (и другие в основном связаны - разница в скорости 1 мс на самом деле не повторяется).
Для того, что стоит, несмотря на то, что эта часть теста проявляется быстрее с g++, общее время было в пределах 1% друг от друга (4.944 секунды для VС++, 4.915 секунд для g++).
Ответ 3
Try
v.assign(v.size(), false);
Посмотрите на эту ссылку:
http://www.cplusplus.com/reference/vector/vector/assign/
Или следующее
std::fill(v.begin(), v.end(), 0)
Ответ 4
Используйте метод std::vector<bool>::assign
, который предоставляется для этой цели.
Если реализация имеет значение для bool
, то assign
, скорее всего, также будет реализована соответствующим образом.
Ответ 5
Если вы можете переключиться с vector<bool>
на представление пользовательского битового вектора, вы можете использовать представление, разработанное специально для быстрых ясных операций, и получить некоторые потенциально довольно значительные ускорения (хотя и не без компромиссов).
Трюк состоит в том, чтобы использовать целые числа для записи вектора бит и одно значение "порогового порога", которое определяет, какие записи на самом деле затем оцениваются как истинные.
Затем вы можете очистить бит-бит, просто увеличив одно пороговое значение, не касаясь остальной части данных (до тех пор, пока порог не переполнится).
Более полная запись об этом и примерный код можно найти здесь.
Ответ 6
Я столкнулся с этим как проблема производительности в последнее время. Я не пробовал искать ответы в Интернете, но нашел, что использование назначения с конструктором было в 10 раз быстрее, используя g++ O3 (Debian 4.7.2-5) 4.7.2. Я нашел этот вопрос, потому что я старался избегать дополнительных malloc
. Похоже, что назначение оптимизировано, а также конструктор и примерно в два раза лучше в моем тесте.
unsigned sz = v.size(); for (unsigned ii = 0; ii != sz; ++ii) v[ii] = false;
v = std::vector(sz, false); // 10x faster
v.assign(sz, false); > // 20x faster
Итак, я бы не сказал, чтобы уклониться от использования специализации vector<bool>
; просто будьте очень осведомлены о представлении вектора бит.
Ответ 7
Кажется, что еще один хороший вариант еще не упоминался:
auto size = v.size();
v.resize(0);
v.resize(size);
Предполагается, что разработчик STL выбрал наиболее эффективные средства обнуления, поэтому нам даже не нужно знать, какой именно метод может быть. И это также работает с реальными векторами (думаю, шаблоны), а не только std::vector<bool>
монстра.
Может быть незначительное добавленное преимущество для повторно используемых буферов в циклах (например, сита, что угодно), где вы просто изменяете размер до того, что будет необходимо для текущего раунда, а не оригинального размера.