web-dev-qa-db-fra.com

utilisez std :: fill pour remplir un vecteur avec des nombres croissants

Je voudrais remplir un vector<int> en utilisant std::fill, mais au lieu d'une valeur, le vecteur devrait contenir des nombres dans l'ordre croissant après.

J'ai essayé d'y parvenir en itérant le troisième paramètre de la fonction, mais cela ne me donnait que des vecteurs remplis avec 1 ou 2 (selon la position de l'opérateur ++).

Exemple:

vector<int> ivec;
int i = 0;
std::fill(ivec.begin(), ivec.end(), i++); // elements are set to 1
std::fill(ivec.begin(), ivec.end(), ++i); // elements are set to 2
50
BlackMamba

Utilisez de préférence std::iota comme ceci:

std::vector<int> v(100) ; // vector with 100 ints.
std::iota (std::begin(v), std::end(v), 0); // Fill with 0, 1, ..., 99.

Cela dit, si vous n'avez aucune assistance c++11 (toujours un problème réel dans lequel je travaille), utilisez std::generate comme ceci:

struct IncGenerator {
    int current_;
    IncGenerator (int start) : current_(start) {}
    int operator() () { return current_++; }
};

// ...

std::vector<int> v(100) ; // vector with 100 ints.
IncGenerator g (0);
std::generate( v.begin(), v.end(), g); // Fill with the result of calling g() repeatedly.
81
BoBTFish

Vous devriez utiliser l'algorithme std::iota:

  std::vector<int> ivec;
  std::iota(ivec.begin(), ivec.end(), 0);

Parce que std::fill attribue simplement la valeur fixe donnée aux éléments de la plage donnée [n1, n2). Et std::iota remplit la plage donnée [n1, n2) avec des valeurs croissantes, en commençant par la valeur initiale puis en utilisant ++value.Vous pouvez également utiliser std::generate comme alternative .

N'oubliez pas que std::iota est l'algorithme C++ 11 STL. Mais beaucoup de compilateurs modernes le supportent, par exemple. GCC, Clang et VS2012: http://msdn.Microsoft.com/en-us/library/vstudio/jj651033.aspx

23
Oleksandr Karaberov

Mon premier choix (même en C++ 11) serait boost::counting_iterator:

std::vector<int> ivec( boost::counting_iterator<int>( 0 ),
                       boost::counting_iterator<int>( n ) );

ou si le vecteur était déjà construit:

std::copy( boost::counting_iterator<int>( 0 ),
           boost::counting_iterator<int>( ivec.size() ),
           ivec.begin() );

Si vous ne pouvez pas utiliser Boost: soit std::generate (comme suggéré dans Autres réponses), soit implémenter counting_iterator vous-même, sivous en avez besoin à différents endroits. (Avec Boost, vous pouvez utiliser Un transform_iterator d'un counting_iterator pour créer toutes sortes de séquences Intéressantes. Sans Boost, vous pouvez en faire beaucoup à la main À la main, soit sous la forme d'un objet générateur. Tapez pour std::generate, ou comme quelque chose que vous pouvez brancher sur un main compter compter itérateur.)

9
James Kanze

Si vous préférez ne pas utiliser les fonctionnalités de C++ 11, vous pouvez utiliser std::generate :

#include <algorithm>
#include <iostream>
#include <vector>

struct Generator {
    Generator() : m_value( 0 ) { }
    int operator()() { return m_value++; }
    int m_value;
};

int main()
{
    std::vector<int> ivec( 10 );

    std::generate( ivec.begin(), ivec.end(), Generator() );

    std::vector<int>::const_iterator it, end = ivec.end();
    for ( it = ivec.begin(); it != end; ++it ) {
        std::cout << *it << std::endl;
    }
}

Ce programme imprime 0 à 9.

5
Frerich Raabe

J'ai vu les réponses avec std :: generate mais vous pouvez aussi "améliorer" cela en utilisant des variables statiques à l'intérieur du lambda, au lieu de déclarer un compteur en dehors de la fonction ou de créer une classe de générateur:

std::vector<int> vec;
std::generate(vec.begin(), vec.end(), [] {
    static int i = 0;
    return i++;
});

Je le trouve un peu plus concis

3
brainsandwich

cela fonctionne aussi 

j=0;
for(std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it){
    *it = j++;
}
2
No Idea For Name

Nous pouvons utiliser generate function qui existe dans le fichier d’en-tête de l’algorithme.

Extrait de code :

#include<bits/stdc++.h>
using namespace std;


int main()
{
    ios::sync_with_stdio(false);

    vector<int>v(10);

    int n=0;

    generate(v.begin(), v.end(), [&n] { return n++;});

    for(auto item : v)
    {
      cout<<item<<" ";
    }
    cout<<endl;

    return 0;
}
2
rashedcs

std :: iota est limité à une séquence n, n + 1, n + 2, ...

Mais que faire si vous voulez remplir un tableau avec une séquence générique f (0), f (1), f (2), etc.? Souvent, nous pouvons éviter un générateur de suivi d'état. Par exemple,

int a[7];
auto f = [](int x) { return x*x; };
transform(a, a+7, a, [a, f](int &x) {return f(&x - a);});

produira la séquence de carrés

0 1 4 9 16 25 36

Cependant, cette astuce ne fonctionnera pas avec d'autres conteneurs.

Si vous êtes bloqué avec C++ 98, vous pouvez faire des choses horribles comme:

int f(int &x) { int y = (int) (long) &x / sizeof(int); return y*y; }

et alors

int a[7];
transform((int *) 0, ((int *) 0) + 7, a, f);

Mais je ne le recommanderais pas. :)

2
pururu

En termes de performances, vous devez initialiser le vecteur en utilisant reserve() associé à Push_back() comme dans l'exemple ci-dessous:

const int numberOfElements = 10;

std::vector<int> data;
data.reserve(numberOfElements);

for(int i = 0; i < numberOfElements; i++)
    data.Push_back(i);

Tous les std::fill, std::generate, etc., fonctionnent sur une plage de contenu de vecteur existant et, par conséquent, le vecteur doit être rempli avec certaines données plus tôt. Même en procédant de la manière suivante: std::vector<int> data(10); crée un vecteur avec tous les éléments définis sur sa valeur par défaut (c.-à-d. 0 dans le cas de int).

Le code ci-dessus évite d’initialiser le contenu vectoriel avant de le remplir avec les données que vous souhaitez réellement. Les performances de cette solution sont bien visibles sur les grands ensembles de données.

1
no one special

J'ai créé une fonction basée sur un modèle, Sequence(), pour générer des séquences de nombres. La fonctionnalité suit la fonction seq() dans R ( link ). Le bon côté de cette fonction est qu’elle permet de générer une variété de séquences et de types de nombres.

#include <iostream>
#include <vector>

template <typename T>
std::vector<T> Sequence(T min, T max, T by) {
  size_t n_elements = ((max - min) / by) + 1;
  std::vector<T> vec(n_elements);
  min -= by;
  for (size_t i = 0; i < vec.size(); ++i) {
    min += by;
    vec[i] = min;
  }
  return vec;
}

Exemple d'utilisation:

int main()
{
    auto vec = Sequence(0., 10., 0.5);
    for(auto &v : vec) {
        std::cout << v << std::endl;
    }
}

Le seul inconvénient est que tous les nombres doivent être du même type inféré. En d'autres termes, pour les doubles ou les flottants, incluez les décimales pour toutes les entrées, comme indiqué.

Mise à jour: 14 juin 2018

0
Adam Erickson

En parlant de boost:

auto ivec = boost::copy_range<std::vector<int>>(boost::irange(5, 10));
0
Mikhail

Je sais que c'est une vieille question, mais je joue actuellement avec library pour traiter exactement ce problème. Cela nécessite c ++ 14.

#include "htl.hpp"

htl::Token _;

std::vector<int> vec = _[0, _, 100];
// or
for (auto const e: _[0, _, 100]) { ... }

// supports also custom steps
// _[0, _%3, 100] == 0, 4, 7, 10, ...
0
Dawid

Si vous vraiment voulez utiliser std::fill et que vous êtes confiné à C++ 98, vous pouvez utiliser quelque chose comme ce qui suit,

#include <algorithm>
#include <iterator>
#include <iostream>
#include <vector>

struct increasing {
    increasing(int start) : x(start) {}
    operator int () const { return x++; }
    mutable int x;
};

int main(int argc, char* argv[])
{
    using namespace std;

    vector<int> v(10);
    fill(v.begin(), v.end(), increasing(0));
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
    cout << endl;
    return 0;
}
0
King Kokonut