Skip to the content of the web site.

Arithmetic sequences

One of the most common arrays used throughout engineering modelling is one with entries $0, 1, 2, 3, 4, ...$. In C++, it seems less obvious as to how to create such an array without an explicit for loop. More generally, one may want to store an arithmetic sequence.

These examples, together with geometric and arbitrary explicitly-defined sequences, can be seen at replit.com.

Using std::iota

The std::iota function takes an argument and assigns that to the first entry of a range, and after each assignment, it increments the argument before going onto the next assignment.

#include <vector>
#include <numeric>

int main();

int main() {
    std::size_t const N{ 20 };
    int array[N];
    std::vector data( 16 );

   // Fill the array with 0, 1, 2, 3, ...
   std::iota( array, array + N, 0 );

   // Fill the std::vector with 0, 1, 2, 3, ...
   std::iota( data.begin(), data.end(), 0 );

   return 0;
}

If you want an arithmetic sequence, you can do the following:

template <typename T>
class Arith;

template <typename T>
class Arith {
  public:
    Arith( T initial, T difference );
    Arith &operator++();
    Arith operator++( int );
    Arith &operator+=( T n );
    T  operator*() const;
    T &operator*();
    operator T() const;

  private:
    T value_;
    T difference_;
};

template <typename T>
Arith<T>::Arith( T initial, T difference ):
value_{ initial },
difference_{ difference } {
  // Empty constructor
}

template <typename T>
Arith<T> &Arith<T>::operator++() {
  value_ += difference_;
  return *this;
}

template <typename T>
Arith<T> Arith<T>::operator++( int ) {
  Arith copy{ *this };
  value_ += difference_;
  return copy;
}

template <typename T>
Arith<T> &Arith<T>::operator+=( T n ) {
  value_ += n*difference_;
  return *this;
}

template <typename T>
T  Arith<T>::operator*() const {
  return value_;
}

template <typename T>
T &Arith<T>::operator*() {
  return value_;
}

template <typename T>
Arith<T>::operator T() const {
  return value_;
}

You can now use this class to initialize a range of entries with an arithmetic sequence:

#include <vector>
#include <numeric>

int main();

int main() {
    std::size_t const N{ 20 };
    int array[N];
    std::vector<int> data( 16 );

    // 3, 5, 7, 9, 11, ...
    std::iota( array, array + N, Arith<int>{3, 2} );
    // 4, 9, 14, 19, 24, ...
    std::iota( data.begin(), data.end(), Arith<int>{4, 5} );

    // Print the array
    for ( std::size_t k{ 0 }; k < N; ++k ) {
        std::cout << array[k] << ", ";
    }

    std::cout << "..." << std::endl;

    // Print the vector
    for ( int value : data ) {
        std::cout << value << ", ";
    }

    std::cout << "..." << std::endl;

    return 0;
}

Using std::generate

The std::generate(...) function calls the argument function successively on each entry of the range. Thus, we may instead have

template <typename T>
class Arithmetic_generator;

template <typename T>
class Arithmetic_generator {
  public:
    Arithmetic_generator( T initial, T difference );
    T operator()();
  private:
    T value_;
    T difference_;
};

template <typename T>
Arithmetic_generator<T>::Arithmetic_generator(
  T initial, T difference
):
value_{ initial },
difference_{ difference } {
  // Empty constructor
}

template <typename T>
T Arithmetic_generator<T>::operator()() {
  T result{ value_ };
  value_ += difference_;
  return result;
}

This can then be used as follows:

#include <vector>
#include <algorithm>

int main();

int main() {
    std::size_t const N{ 20 };
    int array[N];
    std::vector<int> data( 16 );

    // 3, 5, 7, 9, 11, ...
    std::generate( array, array + N, Arithmetic_generator<int>{3, 2} );
    // 4, 9, 14, 19, 24, ...
    std::generate( data.begin(), data.end(), Arithmetic_generator<int>{4, 5} );

    // Print the array
    for ( std::size_t k{ 0 }; k < N; ++k ) {
        std::cout << array[k] << ", ";
    }

    std::cout << "..." << std::endl;

    // Print the vector
    for ( int value : data ) {
        std::cout << value << ", ";
    }

    std::cout << "..." << std::endl;

    return 0;
}