Reshape vector

Hi, my task is to reshape a vector in order to build a matrix.
In particular, let say i have a vector of n elements.
Knowing that the number of element n = a*b, i would like to build a matrix with a rows and b columns.
As example, i have 120 elements, i would like to build a matrix of 12 rows and 10 columns.

Thank's
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <vector>
#include <stdexcept>
#include <numeric>

template < typename T >
std::vector< std::vector<T> > to_2d( const std::vector<T>& flat_vec, std::size_t ncols )
{
    // sanity check
    if( ncols == 0 || flat_vec.size()%ncols != 0 ) throw std::domain_error( "bad #cols" ) ;

    const auto nrows = flat_vec.size() / ncols ;

    std::vector< std::vector<T> > mtx ;
    const auto begin = std::begin(flat_vec) ;
    for( std::size_t row = 0 ; row < nrows ; ++row ) mtx.push_back( { begin + row*ncols, begin + (row+1)*ncols } ) ;
    return mtx ;
}

int main()
{
    std::vector<int> flat(120) ;
    std::iota( std::begin(flat), std::end(flat), 100 ) ;

    for( const auto& row : to_2d( flat, 12 ) ) // 10 x 12
    {
        for( int v : row ) std::cout << v << ' ' ;
        std::cout << '\n' ;
    }

    std::cout << "---------------------------\n" ;

    for( const auto& row : to_2d( flat, 20 ) ) // 6 x 20
    {
        for( int v : row ) std::cout << v << ' ' ;
        std::cout << '\n' ;
    }

    std::cout << "---------------------------\n" ;

    try
    {
        for( const auto& row : to_2d( flat, 17 ) ) // bad ncols
        {
            for( int v : row ) std::cout << v << ' ' ;
            std::cout << '\n' ;
        }
    }
    catch( const std::exception& e ) { std::cerr << "*** error: " << e.what() << '\n' ; }
}

http://coliru.stacked-crooked.com/a/526a926f498d10de
Thank you a lot for the explanation. I'm really new on C++, there is a way to describe more in detail the code?
Thank you.
Vittorio
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <iostream>
#include <vector>
#include <stdexcept>
#include <numeric>

// split a flat vector containing nrows*ncols elements into a two dimensional matrix
// T is (a placeholder for) the type of the values in the vector
template < typename T > // for example, for std:vector<int>, T would be int
std::vector< std::vector<T> > to_2d( const std::vector<T>& flat_vec, std::size_t ncols )
{
    // ncols is the number of cols in the matrix
    // the size of the flat vector must be an integral multiple of some non-zero ncols
    // if the check fails, report an error (by throwing an exception)
    if( ncols == 0 || flat_vec.size()%ncols != 0 ) throw std::domain_error( "bad #cols" ) ;

    // compute the number of rows in the matrix ie. flat_vec.size() == nrows * ncols
    const auto nrows = flat_vec.size() / ncols ;

    // declare an empty matrix. eventually, we build this up to an nrows x ncols matrix
    // the final matrix would be a collection of nrows rows
    // exch row in the matrix would a collection (a vector) containing ncols elements
    std::vector< std::vector<T> > mtx ;

    // get an iterator to the beginning of the flat vector
    // http://en.cppreference.com/w/cpp/iterator/begin
    // if you are unfamiliar with iterators, see: https://cal-linux.com/tutorials/STL.html
    const auto begin = std::begin(flat_vec) ;

    // add rows one by one to the matrix
    for( std::size_t row = 0 ; row < nrows ; ++row ) // for each row [0,nrows-1] in the matrix
    {
        // add the row (a vector of ncols elements)
        // for example, if ncols = 12,
        // row 0 would contain elements in positions 0, 1, 2, ...10, 11 ie. [0,12)
        // row 1 would contain elements in positions 12, 13, ...23 ie. [12,24)
        // row 2 would contain elements in positions 24, 25, ...35 ie. [24,36)
        // in general, row r would contain elements at positions [ r*12, (r+1)*12 ]
        mtx.push_back( { begin + row*ncols, begin + (row+1)*ncols } ) ;
        // the above as akin to:
        // construct the row containing elements at positions as described earlier
        // const std::vector<T> this_row( begin + row*ncols, begin + (row+1)*ncols ) ;
        // mtx.push_back(this_row) ; // and add this row to the back of the vector
    }

    return mtx ; // return the fully populated matrix
}

int main()
{
    std::vector<int> flat(120) ; // vector containing 120 values

    // http://en.cppreference.com/w/cpp/algorithm/iota
    std::iota( std::begin(flat), std::end(flat), 100 ) ; // fill it with 100, 101, 102 etc.

    // to_2d( flat, 12 ) returns a 10 x 12 matrix
    // range based loop: http://www.stroustrup.com/C++11FAQ.html#for
    // auto: http://www.stroustrup.com/C++11FAQ.html#auto
    for( const auto& row : to_2d( flat, 12 ) ) // for each row in the returned matrix
    {
        // print each int in that row
        for( int v : row ) std::cout << v << ' ' ;
        std::cout << '\n' ;
    }
    // note: we could write the above as
    // const auto mtx = to_2d( flat, 12 ) ; // get the matrix returned by the function
    // for( const auto& row : mtx ) // for each row in the matrix
    // { etc...

    std::cout << "---------------------------\n" ;

    for( const auto& row : to_2d( flat, 20 ) ) // 6 x 20
    {
        for( int v : row ) std::cout << v << ' ' ;
        std::cout << '\n' ;
    }

    std::cout << "---------------------------\n" ;

    try // 17 is an invalid value for the number of columns
    {
        // to_2d( flat, 17 ) would report an error by throwing an exception
        // when that happens, control would be transferred to the catch block
        for( const auto& row : to_2d( flat, 17 ) ) // bad ncols
        {
            for( int v : row ) std::cout << v << ' ' ;
            std::cout << '\n' ;
        }
    }
    catch( const std::exception& e ) // catch the error (exception)
    {
        // and print out an error messagr
        std::cerr << "*** error: " << e.what() << '\n' ;
    }
}
Another thought for you:

You don’t actually have to change your data structure. You just need to implement a “view”. It can be simple or complex, however you prefer. Here is a fairly simple one, along with some code to play with it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iomanip>
#include <iostream>
#include <vector>

#define EXPAND0(X) X ## 1
#define EXPAND(X) EXPAND0(X)



// --- >8 snip -----------------------------------------

// A 1D to 2D view
// Missing useful stuff like iterators, etc.

template <typename Container, std::size_t Rows, std::size_t Columns>
struct view_1D_to_2D_type
{
  Container& container;
  view_1D_to_2D_type( Container& container ): container(container) { }
  
  typedef decltype(container[0]) value_type;

  std::size_t index( std::size_t row, std::size_t column ) const
  {
    return row * Columns + column;
  }

  std::size_t rows()    const { return Rows; }
  std::size_t columns() const { return Columns; }

  value_type& operator () ( std::size_t row, std::size_t column ) const { return container   [ index( row, column ) ]; }
  value_type& at          ( std::size_t row, std::size_t column ) const { return container.at( index( row, column ) ); }
};

template <std::size_t Rows, std::size_t Columns, typename Container>
view_1D_to_2D_type <Container, Rows, Columns>
view_1D_to_2D( Container& container )
{
  return view_1D_to_2D_type <Container, Rows, Columns> ( container );
}
// --- snip 8< -----------------------------------------



// Here you can play with the const-ness of the source structures and 
// the reference type acceptable by the function.

#define CONST //const

template <typename Container>
#if   0
void test(       Container&  container )
#elif 0
void test( const Container&  container )
#elif 1
void test(       Container&& container )
#else
void test( const Container&& container )
#endif
{
  for (auto r = 0U; r < container.rows(); r++)
  {
    for (auto c = 0U; c < container.columns(); c++)
      std::cout << " " << std::setw(2) << std::left << container( r, c );
    std::cout << "\n";
  }
  std::cout << "\n";
}

int main()
{
  { std::cout << "test an array\n";
    CONST int xs[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
    test( view_1D_to_2D <3, 4> ( xs ) );
    test( view_1D_to_2D <2, 6> ( xs ) );
    
    #if EXPAND(CONST)
    std::cout << "swapping (1,1) and (2,1):\n";
    auto vs = view_1D_to_2D <4, 3> ( xs );
    std::swap( vs( 1, 1 ), vs( 2, 1 ) );
    test( vs );
    test( view_1D_to_2D <3, 4> ( xs ) );
    test( view_1D_to_2D <2, 6> ( xs ) );
    #endif
  }

  { std::cout << "test a vector\n";
    CONST std::vector <int> xs { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
    test( view_1D_to_2D <3, 4> ( xs ) );
    test( view_1D_to_2D <2, 6> ( xs ) );
    
    #if EXPAND(CONST)
    std::cout << "swapping (1,1) and (2,1):\n";
    auto vs = view_1D_to_2D <4, 3> ( xs );
    std::swap( vs( 1, 1 ), vs( 2, 1 ) );
    test( vs );
    test( view_1D_to_2D <3, 4> ( xs ) );
    test( view_1D_to_2D <2, 6> ( xs ) );
    #endif
  }
}

There might be something in Boost that does this too.
(I haven’t looked lately.)
> There might be something in Boost that does this too

boost::multi_array_ref and boost::const_multi_array_ref have been around for a long time.
http://www.boost.org/doc/libs/1_66_0/libs/multi_array/doc/reference.html#multi_array_ref

C++17 : lines 35 and 47 uses class template argument deduction (legacy C++: change to matrix_view<int>)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <boost/multi_array.hpp>
#include <vector>
#include <array>
#include <numeric>

template < typename T > struct matrix_view : boost::multi_array_ref<T,2>
{
     using base = boost::multi_array_ref<T,2> ;

     // construct from vector (size check elided for brevity)
     matrix_view( std::vector<T>& seq, std::size_t nrows, std::size_t ncols )
         : base( std::addressof( seq.front() ), boost::extents[nrows][ncols] ) {}

     template < std::size_t N > // construct from std array (size check elided for brevity)
     matrix_view( std::array<T,N>& seq, std::size_t nrows, std::size_t ncols )
         : base( std::addressof( seq.front() ), boost::extents[nrows][ncols] ) {}

     template < std::size_t N > // construct from c-style array (size check elided for brevity)
     matrix_view( T (&raw_array)[N], std::size_t nrows, std::size_t ncols )
         : base( raw_array[0], boost::extents[nrows][ncols] ) {}

     matrix_view( T* ptr, std::size_t nrows, std::size_t ncols ) // construct from raw pointer
         : base( ptr, boost::extents[nrows][ncols] ) {}

     using base::base ; // other constructors are inherited
};

int main()
{
    std::vector<int> vec(500) ;
    std::iota( std::begin(vec), std::end(vec), 100 ) ;

    {
        matrix_view mv( vec, 5, 8 ) ; // 5x8 view of the first 40 elements of the vector

        mv[1][2] = mv[2][3] = mv[3][4] = -99 ;

        for( const auto& row : mv )
        {
            for( int v : row ) std::cout << v << ' ' ;
            std::cout << '\n' ;
        }
    }
    std::cout << '\n' ;
    {
        matrix_view mv( std::addressof(vec[10]), 6, 4 ) ; // 6x4 view of elements [10,33] of the vector

        for( const auto& row : mv )
        {
            for( int v : row ) std::cout << v << ' ' ;
            std::cout << '\n' ;
        }
    }
}

http://coliru.stacked-crooked.com/a/69055dffd6ae34e6
Thank you for the help! Very useful suggestions!
hopefully not repeating anything here but...
matlab's reshape is much easier to do if you use a 1-d container instead of 2-d.
then you just change your class rows/cols members, resize the vector if needed, and shuffle the data to its new locations. If its not too late, 1-d approach simplifies several operations that are a slow to execute and annoying to code in 2-d, including transpose, Identy-append-invert, AXB /RREF solve, reshape, and similar things that can affect the size of your matrix directly.

I know quite well matlab and for a fist test i used the function reshape that is really useful. Anyway for my application i have to use c++ program.
I think you misread that.
I was saying that to reproduce something like matlab's reshape, use a 1-d construct. In c++. Not that you should use matlab, its a 1/2 million dollar solution to a 10 cent problem :P

using 2-d constructs creates extra work for a number of common operations like this one. Take an extreme example of a 5 by 5 million matrix. To reshape that in 2d, you have to reallocate all the 5s to 5 million and reallocate all the 5 millions down to 5. In 1d, the number of cells is the same, only the way the data is stored is changed, so a O(N) pass doing some swaps and then swap num_rows and num_cols. No memory activity, much faster and cleaner.
Last edited on
> to reproduce something like matlab's reshape

boost::multi_array does support both reshape() and reindex()
http://www.boost.org/doc/libs/1_66_0/libs/multi_array/doc/reference.html#multi_array_class

Though, in well written code, reshape/reindex should be rarely used.

When it is easy to create any number of simultaneous multiple views of the same array, a component that requires a different shape should just create (and, if appropriate, keep) a custom view for its own use.

Instead of unilaterally reshaping the array; instead of insisting that every other component in the program must now start viewing the array in the same way that this anti-social component wants to look at it.
Or, (only in a single threaded program,) incurring the overhead of reshaping the array at the beginning of every function, and then restoring the original shape at the end, all done in an exception safe manner.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <boost/multi_array.hpp>
#include <cassert>

int main()
{
    int a[2][3][4][5] {} ; // original raw array: 2x3x4x5
    auto p_first = std::addressof( a[0][0][0][0] ) ;

    // 1D view of the same array
    boost::multi_array_ref<int,1> v120( p_first, boost::extents[120] ) ;
    v120[46] = 999 ;

    // different 2D views of the same array
    boost::multi_array_ref<int,2> v15x8( p_first, boost::extents[15][8] ) ;
    boost::multi_array_ref<int,2> v6x20( p_first, boost::extents[6][20] ) ;
    assert( v120[46] == 999 && v15x8[5][6] == v120[46] && v15x8[5][6] == v6x20[2][6] ) ;

    // different 3D views of the same array
    boost::multi_array_ref<int,3> v5x4x6( p_first, boost::extents[5][4][6] ) ;
    boost::multi_array_ref<int,3> v10x6x2( p_first, boost::extents[10][6][2] ) ;
    assert( v10x6x2[3][5][0] == 999 ) ;

    // different 4D views of the same array
    boost::multi_array_ref<int,4> v4x3x5x2( p_first, boost::extents[4][3][5][2] ) ;
    boost::multi_array_ref<int,4> v2x6x2x5( p_first, boost::extents[2][6][2][5] ) ;

    // different 5D views of the same array
    boost::multi_array_ref<int,5> v2x3x2x5x2( p_first, boost::extents[2][3][2][5][2] ) ;
    boost::multi_array_ref<int,5> v5x2x2x3x2( p_first, boost::extents[5][2][2][3][2] ) ;
}

http://coliru.stacked-crooked.com/a/2eaaedc12e5b5390
Topic archived. No new replies allowed.