Comment mettre à zéro un vecteur?

J'ai un vector<bool> et je voudrais le mettre à zéro. J'ai besoin de la taille pour rester la même.

L'approche normale consiste à itérer sur tous les éléments et à les réinitialiser. Cependant, vector<bool> est un conteneurspécialement optimisé qui, selon l'implémentation, ne peut stocker qu'un seul bit par élément. Y a-t-il un moyen d'en profiter pour effacer le tout efficacement?

bitset, la variante de longueur fixe, a la fonction set. vector<bool> a-t-il quelque chose de similaire?

21
demandé sur Kerrek SB 2013-10-24 13:55:32

7 réponses

Vous n'avez pas de chance. std::vector<bool> est une spécialisation qui apparemment ne garantit même pas la mémoire contiguë ou les itérateurs à accès aléatoire (ou même en avant?!), au moins sur la base de ma lecture de cppreference - décoder la norme serait l'étape suivante.

Alors écrivez du code spécifique à l'implémentation, priez et utilisez une technique de réduction à zéro standard, ou n'utilisez pas le type. Je vote 3.

La sagesse reçue est que c'était une erreur, et peut devenir obsolète. Utilisez un conteneur différent si possible. Et certainement ne plaisante pas avec les tripes internes, ou compter sur son emballage. Vérifiez si vous avez un bitset dynamique dans votre bibliothèque std mayhap, ou roulez votre propre wrapper autour de std::vector<unsigned char>.

9
répondu Yakk - Adam Nevraumont 2013-10-24 11:19:05

Il semble y avoir beaucoup de suppositions, mais très peu de faits dans les réponses qui ont été affichées jusqu'à présent, alors peut-être qu'il serait utile de faire un peu de test.

#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";
}

Cela crée donc un vecteur, y place des bits sélectionnés au hasard, les compte et les efface (et les répète). Le réglage / comptage / impression est fait pour s'assurer que même avec une optimisation agressive, le compilateur ne peut / n'optimisera pas notre code pour effacer le vecteur.

J'ai trouvé les résultats intéressants, à dire le moins. D'abord le résultat avec VC++:

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

Donc, avec VC++, la méthode la plus rapide est ce que vous penseriez probablement au départ comme la plus naïve-une boucle qui attribue à chaque élément individuel. Avec g++, les résultats sont juste un tad cependant différent:

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

Ici, la boucle est (de loin) la méthode la plus lente (et les autres sont fondamentalement liées-la différence de vitesse de 1 ms n'est pas vraiment répétable).

Pour ce que ça vaut, malgré cette partie du test montrant comme beaucoup plus rapide avec g++, les temps globaux étaient à moins de 1% l'un de l'autre (4.944 secondes pour VC++, 4.915 secondes pour g++).

18
répondu Jerry Coffin 2013-10-24 10:53:31

Essayez

v.assign(v.size(), false);

Jetez un oeil à ce lien: http://www.cplusplus.com/reference/vector/vector/assign/

Ou

std::fill(v.begin(), v.end(), 0)
11
répondu Kakalokia 2013-10-24 10:08:26

Si vous êtes en mesure de passer de vector<bool> à une représentation vectorielle de bits personnalisée, vous pouvez utiliser une représentation conçue spécifiquement pour les opérations d'effacement rapide, et obtenir des accélérations potentiellement assez importantes (mais pas sans compromis).

L'astuce consiste à utiliser des entiers par entrée vectorielle de bits et une seule valeur de "seuil de roulement" qui détermine les entrées réellement évaluées à true.

Vous pouvez alors effacer le vecteur de bits en augmentant simplement le seuil unique valeur, sans toucher le reste des données (jusqu'à ce que le seuil déborde).

Plus complète écrire à ce sujet, et un exemple de code, peut être trouvé ici.

5
répondu Thomas Young 2014-07-01 13:50:43

J'ai rencontré cela comme un problème de performance récemment. Je n'avais pas essayé de chercher des réponses sur le web mais j'ai trouvé que l'utilisation d'assignment avec le constructeur était 10 fois plus rapide en utilisant g++ O3 (Debian 4.7.2-5) 4.7.2. J'ai trouvé cette question parce que je cherchais à éviter le malloc supplémentaire. On dirait que l'assign est optimisé ainsi que le constructeur et environ deux fois plus bon dans mon benchmark.

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

Donc, je ne dirais pas d'hésiter à utiliser la spécialisation de vector<bool>; juste être très conscient de la représentation vectorielle de bits.

5
répondu rwp 2016-04-07 17:36:17

Utilisez la méthode std::vector<bool>::assign, qui est fournie à cet effet. Si une implémentation est spécifique à bool, alors assign, très probablement, également implémentée de manière appropriée.

5
répondu Andrii 2017-12-18 13:00:04

Il semble qu'une bonne option n'ait pas encore été mentionnée:

auto size = v.size();
v.resize(0);
v.resize(size);

L'implémenteur de STL aura soi-disant choisi le moyen le plus efficace de zéroiser, donc nous n'avons même pas besoin de savoir quelle méthode particulière cela pourrait être. Et cela fonctionne aussi avec des vecteurs réels (pensez aux modèles), pas seulement la monstruosité std::vector<bool>.

Il peut y avoir un minuscule avantage supplémentaire pour les tampons réutilisés dans les boucles (par exemple tamis, peu importe), où vous redimensionnez simplement à ce qui sera nécessaire pour le rond actuel, au lieu de la taille d'origine.

2
répondu DarthGizka 2016-05-13 14:11:41