Populate a vector with linearly increased values

2019-08-17 23:03发布

问题:

I'd to populate/initialize a vector with linearly increased value. For example, for every element in this vector, I'd like the next one is a more than the previous. Or the kth element has a value of k*a

something like this:

float a = 1.132;
vector<float> v(100);

for (int n = 0; n < 100; ++n)
{
    v[n] = n*a;
}

Is there a more elegant way to do it? Thanks.

A matlab example would be the linspace(beginning value, end value, number of points)

linspace(1,5, 6)

ans =

    1.0000    1.8000    2.6000    3.4000    4.2000    5.0000

回答1:

The first thing you could go is switch to using std::generate or std::generate_n instead of a for loop. The generate version could look like

int main()
{
    float a = 1.132;
    std::vector<float> v(100);
    std::generate(v.begin(), v.end(), [n = 0, &a]() mutable { return n++ * a; });
}

Another option is to create an iterator that will generate the value as you iterate it. This has the advantage that you do not need to initialize v with any default constructed valued (this can be/is expensive). Then you use the vectors range constructor and it will initialize all of the elements. As long as the iterator abides by the forward iterator requirements then the vector will figure out the space needed (which if it is not random access causes a full iteration), allocate, and then initialize(full iteration). This could be expensive with the double iteration so it might not be any faster and could be slower then the generate case (since zero initializing is pretty fast).



回答2:

If you have access to C++11 you can use std::generate with a lambda :

std::generate(v.begin(), v.end(), [n = 0, &a] () mutable { return a * n++; });

However, for this use case a vector is hardly needed as said in the comments.



回答3:

You can define a type that has an operator++ and a conversion to float, and use std::iota

#include <algorithm>
#include <vector>

struct spacer
{
    spacer(float scale, int count = 0) : scale(scale), count(count) {}
    spacer operator++(){ ++count; return *this; }
    operator float(){ return count * scale; }
private:
    float scale;
    int count;
};

int main()
{
    std::vector<float> v(100);
    std::iota(v.begin(), v.end(), spacer(1.132));
}

If you have access to boost, you can instantiate your vector using that and boost::counting_iterator

#include <algorithm>
#include <vector>
#include <tuple>
#include <boost/iterator/counting_iterator.hpp>

struct spacer
{
    spacer(float scale, int count = 0) : scale(scale), count(count) {}
    spacer operator++(){ ++count; return *this; }
    operator float() const{ return count * scale; }
    bool operator==(const spacer & rhs) const{ return std::tie(count, scale) == std::tie(rhs.count, rhs.scale); }
private:
    float scale;
    int count;
};

int main()
{
    using iter = boost::counting_iterator<spacer, std::forward_iterator_tag, std::ptrdiff_t>;
    std::vector<float> v(iter(spacer(1.132)), iter(spacer(1.132, 100)));
}


回答4:

If you are looking for something like the matlab linspace, it's not directly available in C++ but it's easy to write a function doing that. Like:

std::vector<float> linspace(float start, float end, size_t points)
{
  std::vector<float> res(points);
  float step = (end - start) / (points - 1);
  size_t i = 0;
  for (auto& e : res)
  {
    e = start + step * i++;
  }
  return res;
}

Then you can use it like:

int main()
{
  std::vector<float> v = linspace(1, 5, 6);

  for (auto f : v) std::cout << f << " ";
  std::cout << std::endl;

  return 0;
}

Output:

1 1.8 2.6 3.4 4.2 5


标签: c++ vector