Is there a simple method in C++ that allows to sum up the vectors componing a matrix element by element? I mean, if I have the matrix M[3][4], I want the sum[3] vector with this components:
sum[0]=M[0][0]+M[1][0]+M[2][0]
sum[1]=M[0][1]+M[1][1]+M[2][1]
sum[2]=M[0][2]+M[1][2]+M[2][2]
I find out that exists this method for two vectors, I would like to extend it to many vectors, in particular to the std::vector<type>rows componing the matrix std::vector<vector<type> > M, not knowing a-priori the dimomension of M.
Is there a simple method in C++ that allows to sum up the vectors componing a matrix element by element? I mean, if I have the matrix M[3][4], I want the sum[3] vector with this components:
sum[0]=M[0][0]+M[1][0]+M[2][0]
sum[1]=M[0][1]+M[1][1]+M[2][1]
sum[2]=M[0][2]+M[1][2]+M[2][2]
Unfortunately, there is no simple method in the C++ standard library for adding
a container's elements column-wise.
There is std::accumulate(), but naturally, it would add up the row elements together, not the column elements, but we can fix that by iterating through each column index inside the matrix and adding up the elements one by one:
#include <iostream>
#include <numeric>
#include <cstddef>
#include <vector>
// The below function assumes that all columns of your matrix have the same length
template <typename T>
std::vector<T> m_col_add(std::vector<std::vector<T>> const& mat) {
std::vector<T> res;
const auto column_size = mat[0].size();
for (size_t x = 0; x < column_size; ++x)
res.push_back(std::accumulate(mat.begin(), mat.end(), T{}, [x](T const& a, std::vector<T> const& row) {
return a + row[x];
}));
return res;
}
int main() {
std::vector<std::vector<int>> mat {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
auto res = m_col_add(mat);
for (auto const& elem : res)
std::cout << elem << " ";
}
Output:
15 18 21 24
Alternatively, if you already know the sizes of the rows and columns during compile-time and are using C++17 or above, you can use fold expressions and std::index_sequence<> to add up the elements faster by compile-time expansion:
#include <iostream>
#include <utility>
#include <cstddef>
#include <array>
template <size_t Column, typename T, size_t Rows, size_t Columns, size_t ...Sizes>
T m_col_add_impl2(std::index_sequence<Sizes...>, std::array<std::array<T, Columns>, Rows> const& mat) {
return (mat[Sizes][Column] + ...);
}
template <typename T, size_t Rows, size_t Columns, size_t ...Sizes>
std::array<T, Columns> m_col_add_impl1(std::index_sequence<Sizes...>, std::array<std::array<T, Columns>, Rows> const& mat) {
std::array<T, Columns> sum;
((sum[Sizes] = m_col_add_impl2<Sizes>(std::make_index_sequence<Rows>(), mat)), ...);
return sum;
}
template <typename T, size_t Rows, size_t Columns>
std::array<T, Columns> m_col_add(std::array<std::array<T, Columns>, Rows> const& mat) {
return m_col_add_impl1(std::make_index_sequence<Columns>(), mat);
}
int main() {
std::array mat {
std::array {1, 2, 3, 4},
std::array {5, 6, 7, 8},
std::array {9, 10, 11, 12}
};
auto res = m_col_add(mat);
for (auto const& elem : res)
std::cout << elem << " ";
}
Output:
15 18 21 24
You could define operator+= for vectors which allows you to define a generalized Sum function that receives begin and end operators of a range to sum that works for iterators with any value type that works with the += operator like primitive types or (possibly nested) vectors of types that provide a += operator:
template<typename T>
std::vector<T>& operator+=(std::vector<T>& lhs, std::vector<T> const& rhs)
{
if (lhs.empty())
{
lhs = rhs; // first summand; 0 + x = x
}
else
{
for (size_t i = 0; i < lhs.size(); ++i)
{
lhs[i] += rhs.at(i); // component-wise add
}
}
return lhs;
}
// sum up elements in a given range
template<typename IteratorType>
typename std::iterator_traits<IteratorType>::value_type Sum(IteratorType summandBegin, IteratorType summandEnd)
{
typename std::iterator_traits<IteratorType>::value_type sum {}; // 0 or object created with default constructor depending on the type
while (summandBegin != summandEnd)
{
sum += *summandBegin;
++summandBegin;
}
return sum;
}
// sums the first count elements of a vector
template<typename T>
T PartialSum(std::vector<T> const& vec, size_t count)
{
assert(vec.size() >= count);
return Sum(vec.begin(), vec.begin() + count);
}
// logic for displaying
template<typename T>
std::ostream& operator<<(std::ostream& s, std::vector<T> const& vec)
{
s << '{';
for (auto& val : vec)
{
s << val << ", ";
}
s << '}';
return s;
}
int main(int argc, char *argv[]) {
std::vector<std::vector<std::vector<int>>> matrix { {{1, 2},{ 3, 4},{ 5, 6}},{{7, 8},{ 9, 10},{11, 12}},{{13, 14},{ 15, 16},{ 17, 18}}};
auto partialSum = PartialSum(matrix, 2);
std::cout << matrix << '\n' << partialSum << '\n';
std::cout << "\nSomething extra:\n";
std::vector<std::vector<std::string>> m2 = {{"Hell" , "Wo"} , {"o ", "rld!"}, {"foo", "bar"}};
auto message = PartialSum(m2, 2);
for (auto& msg : message)
{
std::cout << msg;
}
std::cout << '\n';
return 0;
}
Related
This question already has answers here:
Passing an array by reference
(5 answers)
Passing a 2D array to a C++ function
(17 answers)
C++ pass an array by reference
(7 answers)
Closed last month.
hello i want to find a way to replace auto keyword in the following code.
#include <iostream>
using namespace std;
void printMatrix(const auto & matrix) {
/* print matrix using range-based for */
}
int main() {
int matrix[][3] = {{}, {}, {}};
int matrix2[][6] = {{}, {}, {}};
printMatrix(matrix);
printMatrix(matrix2);
return 0;
}
what should i use to replace auto in const auto & matrix.
i can use pointers but the problem is i have to pass the rows and columns size.
the upper code is working but i want to know how the auto keyword handle this.
This function declaration
void printMatrix(const auto & matrix) {
/* print matrix using range-based for */
}
declares a template function.
Instead you could write for example
template <typename T, size_t M, size_t N>
void printMatrix(const T ( & matrix)[M][N]) {
/* print matrix using range-based for */
}
and the function is called as the previous function
printMatrix(matrix);
printMatrix(matrix2);
As the element type of the arrays is known then you can also write
template <size_t M, size_t N>
void printMatrix(const int ( & matrix)[M][N]) {
/* print matrix using range-based for */
}
//...
printMatrix(matrix);
printMatrix(matrix2);
Within the function you can use the values M and N in nested for loops to output the arrays as for example
for ( size_t i = 0; i < M; i++ )
{
for ( size_t j = 0; j < N; j++ )
{
std::cout << matrix[i][j] << ' ';
}
std::cout << '\n';
}
Or you can use range-based for loop
for ( const auto &row : matrix )
{
for ( const auto &item : row )
{
std::cout << item << ' ';
}
std::cout << '\n';
}
You could do the same using the initial function like for example
#include <iterator>
//...
void printMatrix(const auto & matrix) {
for ( size_t i = 0, m = std::size( matrix ); i < m; i++ )
{
for ( size_t j = 0, n = std::size( matrix[i] ); j < n; j++ )
{
std::cout << matrix[i][j] << ' ';
}
std::cout << '\n';
}
}
Here is a demonstration program.
#include <iostream>
#include <iterator>
std::ostream & printMatrix( const auto &matrix, std::ostream &os = std::cout )
{
for (size_t i = 0, m = std::size( matrix ); i < m; i++)
{
for (size_t j = 0, n = std::size( matrix[i] ); j < n; j++)
{
os << matrix[i][j] << ' ';
}
os << '\n';
}
return os;
}
int main()
{
int matrix[][3] =
{
{ 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }
};
int matrix2[][6] =
{
{ 0, 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 0, 1 }, { 2, 3, 4, 5, 6, 7 }
};
printMatrix( matrix ) << '\n';
printMatrix( matrix2 ) << '\n';
}
The program output is
0 1 2
3 4 5
6 7 8
0 1 2 3 4 5
6 7 8 9 0 1
2 3 4 5 6 7
Re-declare the print function to allow only a function parameter that is a 2D-array by reference, for a parameterized element type:
#include <cstddef> // std::size_t
template<typename T, std::size_t num_rows, std::size_t num_cols>
void printMatrix(T const (&mat)[num_rows][num_cols]) {
/* print matrix using range-based for */
}
// ....
printMatrix(matrix); // template arguments inferred as <int, 3, 3>
printMatrix(matrix2); // template arguments inferred as <int, 3, 6>
This is essentially a more specialized version as compared to an overload using a single type template parameter (as in OP's example, via auto/abbreviated function template with a single invented type template parameter).
A good replacement can be with a concept.
From here:
template<typename T>
concept nested_range = std::ranges::range<T> && std::ranges::range<std::ranges::range_value_t<T>>;
So
template <nested_range Matrix>
void printMatrix(const Matrix & matrix) {
for (const auto& line: matrix) {
for (const auto& x: line) {
std::cout << x << " ";
}
std::cout << "\n";
}
}
This way, it is shown in the code that only an iterable (concept range) containing an iterable (e.g. 2D-array) is expected and a reasonable error message will be given for wrong parameters.
Live example with more than enough includes.
You can replace auto with a template parameter.
That's how it was done before C++ 14.
#include <iostream>
using namespace std;
template <typename Matrix>
void printMatrix(const Matrix & matrix) {
/* print matrix using range-based for */
}
int main() {
int matrix[][3] = {{}, {}, {}};
int matrix2[][6] = {{}, {}, {}};
printMatrix(matrix);
printMatrix(matrix2);
return 0;
}
I have a simple program:
#include <array>
#include <iostream>
#include <functional>
#include <algorithm>
using namespace std;
int main(){
array<int, 5> myArr = {3, 10, 0, 5, 7};
int badNum = 0;
for(int item : myArr){
cout << item << endl;
}
cout << "\n" << endl;
cout << "\n" << endl;
sort(myArr.begin(), myArr.end(), greater<int>());
for(int item : myArr){
cout << item << endl;
}
array<int, 4> goodFour;
for (unsigned int i = 0; i < myArr.size(); i++){
if(myArr[i] != badNum){
// goodThree.append(myArr[i]); <-- This is where I am stuck
}
}
}
I am stuck on trying to assign an element to a std::array. I know in std::vector I can use push_back method, but on a std:array, how to assign to the next (not yet assigned) element? I am coming from Python 3.x where we have the append method for a list. I am not attempting to change the size of the array, instead, I am trying to fill in the allocated spaces with values.
I have looked at:
http://www.cplusplus.com/forum/beginner/67707/
http://www.cplusplus.com/forum/beginner/86394/
http://www.cplusplus.com/forum/beginner/152125/
But these are all for vectors or the primitive int[5] myArr types, not std::array.
If you only want the first three good numbers you would maintain a pointer to the next index to insert at:
for (int i = 0, k = 0; k < 3 && i < myArr.size(); i++) {
if (myArr[i] != badNum) {
goodThree[k++] = myArr[i];
}
}
If you want all the numbers that are good you would use a std::vector and call its push_back method:
std::vector<int> goodThree;
for (int i = 0; i < myArr.size(); i++) {
if (myArr[i] != badNum) {
goodThree.push_back(myArr[i]);
}
}
The size of std::array is fixed at compile time. If you need to append another value at run time, then you have to use std::vector (or something like it). The closest you can get to "appending" to a std::array is if you copy its contents into an array with one more element that contains the "appended" value. You can use some template metaprogramming techniques to make this easier:
template <typename T, std::size_t N, typename X, std::size_t... I>
std::array<T, N + 1> push_back(std::array<T, N> const& a, X&& x, std::index_sequence<I...>) {
return {std::get<I>(a)..., std::forward<X>(x)};
}
template <typename T, std::size_t N, typename X>
std::array<T, N + 1> push_back(std::array<T, N> const& a, X&& x) {
return detail::push_back(a, std::forward<X>(x), std::make_index_sequence<N>());
}
Example of use:
std::array<int, 2> a = {1, 2};
std::array<int, 3> b = push_back(a, 3);
for (int x : b) {
std::cout << x << "\n";
}
The size of a std::array is set at compile time. This means that you cannot "append" values to a std::array at run time. However, you could track how many values your array "contains" by your own program logic. For example:
std::array<int, 5> a = {1, 2, 3, 4, 5};
std::array<int, 4> b;
std::size_t n = 0;
for (int x : a) {
if (x != 2) {
if (n < std::size(a) - 1) {
b[n++] = x;
} else {
throw std::out_of_range("array is full");
}
}
}
I need both the minimum and the mean of the values in a vector.
I'm computing them separately with the following:
template <class T>
T Minimum(std::vector<T> & v){ return *min_element(begin(v), end(v)); }
template <class T>
T Mean(std::vector<T> & v)
{
T sum = std::accumulate(v.begin(), v.end(), static_cast<T>(0));
T mean = sum / v.size();
return mean;
}
Both of these have to sweep the vector.
Is there a std efficient way to compute both minimum and mean of a vector sweeping it only once?
Yes, you can accumulate the minimum and the sum in the same call. No, it probably won't be more efficient, nor will it be less efficient.
template <typename T>
std::pair<T, T> MeanAndMin(const std::vector<T> & v)
{
auto zero = std::make_pair(static_cast<T>(0), std::numeric_limits<T>::max());
auto plus = [](auto pair, auto elem) { return std::make_pair(pair.first + elem, std::min(pair.second, elem)); };
auto res = std::accumulate(begin(v), end(v), zero, plus);
res.first /= v.size();
return res;
}
You can use std::accumulate, paired with custom functors:
#include <iostream>
#include <vector>
#include <numeric>
#include <limits>
#include <algorithm>
struct average_and_min {
int sum = 0;
int min = std::numeric_limits<int>::max();
std::size_t num_of_elements = 0;
int get_sum() {
return sum;
}
double get_average() {
return static_cast<double>(sum) / num_of_elements;
}
int get_min() {
return min;
}
};
int main() {
std::vector<int> vec = {1, 2, 5, 4, 4, 2, 4};
auto func_accumulate = [](average_and_min acc, int value) {
return average_and_min{acc.sum + value, std::min(acc.min, value), acc.num_of_elements + 1};
};
auto data = std::accumulate(vec.cbegin(), vec.cend(), average_and_min{}, func_accumulate);
std::cout << "avg: " << data.get_average() << '\n'
<< "min: " << data.get_min() << '\n';
}
EDIT:
As #Caleth suggested in the comments, it might be a good idea not to use lambdas to combine your struct and the value - you can overload operator + inside average_and_min like so:
average_and_min operator + (int value) {
return average_and_min{sum + value, std::min(min, value), num_of_elements + 1};
}
and the line
auto data = std::accumulate(vec.cbegin(), vec.cend(), average_and_min{}, func_accumulate);
can now become
auto data = std::accumulate(vec.cbegin(), vec.cend(), average_and_min{});
I am trying to rotate a vector of Vectors of chars.
I made a 2d vector matrix setup. right now the matrix takes input from a file, I use vector.push_back(c) to add the characters to the vvc;
An example of the vvc array would be something like this
aaaaa
azzza
azaza
azzza
azaaa
azaaa
azaaa
aaaaa
I have the vvc setup, But I am trying to rotate it 90 degrees. I rotated it 90 degrees counter clockwise but I need to rotate it 90 degrees clockwise.
as of right now my code does this
90 counter clock
aaaaaaaa
azzzzzza
azazaaaa
azzzaaaa
aaaaaaaa
and it does it through this loop;
cout <<"\n90 counter clock"<<endl;
for (size_t colNo = 0; colNo < kvsize2; colNo++)
{
for (const auto &row : twovector)
{
char colVal = row.at(colNo);
cout << colVal;
}
cout << endl;
}
I am just learning about vectors, and the range for. Trying to do a decrement loop almost works, but keeps throwing me into a segfault.
"Solved"
I was using
twovector.push_back(temp);
using
twovector.insert(twovector.begin(),temp);
gives me
90 counter clock
aaaaaaaa
azzzzzza
aaaazaza
aaaazzza
aaaaaaaa
Tackling a specific part of the question :
If anyone has any tips or suggestions on how to rotate a M*N 2d vector array
C++ is good at segregating algorithms from data.
Kindly note that the answer is a bit lengthy and has been written with the objective of a tutorial.
Lets begin !!
We want 3 features from our rotate_2d_matrix_clockwise algorithm :
It should work with all datatypes, i.e. int, char, double or any user defined type.
It should work with different types of containers, such as std::array and std::vector
It should be chain-able, i.e. user should be able to call rotate_2d_matrix_clockwise on the result returned by rotate_2d_matrix_clockwise, to achieve 2 times rotation.
Once we are clear with our requirements, we can draft some use-cases for our algorithm.
std::vector<std::vector<char>> data = { {'a', 'b', 'c', 'd'},
{'e', 'f', 'g', 'h'},
{'i', 'j', 'k', 'l'} };
rotate_2d_matrix_clockwise(data); // rotating 2d-matrix of vector<char>
std::array<std::array<int, 4>, 3> data2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
// rotating 2d-matrix of array<int>, twice
rotate_2d_matrix_clockwise(rotate_2d_matrix_clockwise(data2)));
So lets use some templates to create a generic 2d-clockwise-rotate function.
Our rotate_2d_matrix_clockwise will :
take the original_matrix and return a new rotated_matrix.
automatically deduce the dimensions i.e. M x N of the container passed to it.
create the rotated_matrix and pass it to a helper function rotate_2d_matrix_clockwise_impl where the actual work would be done.
So how will the implementation of rotate_2d_matrix_clockwise for std::array look ?
template<typename T, size_t M, size_t N>
auto rotate_2d_matrix_clockwise(std::array<std::array<T, M>, N> const & original_matrix)
-> std::array<std::array<T, N>, M>
{
std::array<std::array<T, N>, M> rotated_matrix;
rotate_2d_matrix_clockwise_impl(original_matrix, rotated_matrix, M, N); // rotate
return rotated_matrix;
}
Neat and precise.
The implementation of rotate_2d_matrix_clockwise for std::vector is a bit messy, though.
template<typename Matrix2D>
auto rotate_2d_matrix_clockwise(Matrix2D const & original_matrix) -> Matrix2D
{
int const M = original_matrix[0].size(); // deduce M and N
int const N = original_matrix.size();
Matrix2D rotated_matrix; // vector has no form, hence we have to resize it for `N x M`
rotated_matrix.resize(M);
for (auto x = 0; x < M; ++x) {
rotated_matrix[x].resize(N);
}
rotate_2d_matrix_clockwise_impl(original_matrix, rotated_matrix, M, N); // rotate
return rotated_matrix;
}
Now lets look at how the actual rotation algorithm rotate_2d_matrix_clockwise_impl would look.
It should be noted, that the algorithm is independent of the container and/or the data contained. It just focuses on rotating.
template<typename OriginalMatrix2D, typename RotatedMatrix2D>
void rotate_2d_matrix_clockwise_impl(OriginalMatrix2D const & original_matrix,
RotatedMatrix2D & rotated_matrix,
int const M,
int const N)
{
for (auto x = 0; x < N; ++x) {
for (auto y = 0; y < M; ++y) {
// Source : https://stackoverflow.com/questions/4780119/2d-euclidean-vector-rotations
rotated_matrix[y][-x -1 +N] = original_matrix[x][y];
}
}
}
Here is a full working example compiled in C++11.
#include <iostream>
#include <vector>
#include <array>
template<typename Matrix2D>
void print_matrix(Matrix2D const & vec)
{
std::cout << "size of matrix is [" << vec[0].size() << " x " << vec.size() << "]\n";
for (auto const & inner_vec : vec) {
for (auto const & item : inner_vec) {
std::cout << item << ", ";
}
std::cout << std::endl;
}
}
template<typename OriginalMatrix2D, typename RotatedMatrix2D>
void rotate_2d_matrix_clockwise_impl(OriginalMatrix2D const & matrix,
RotatedMatrix2D & rotated_matrix,
int const M,
int const N)
{
for (auto x = 0; x < N; ++x) {
for (auto y = 0; y < M; ++y) {
// Source : https://stackoverflow.com/questions/4780119/2d-euclidean-vector-rotations
rotated_matrix[y][-x -1 +N] = matrix[x][y];
}
}
}
template<typename T, size_t M, size_t N>
auto rotate_2d_matrix_clockwise(std::array<std::array<T, M>, N> const & original_matrix)
-> std::array<std::array<T, N>, M>
{
std::array<std::array<T, N>, M> rotated_matrix;
rotate_2d_matrix_clockwise_impl(original_matrix, rotated_matrix, M, N);
return rotated_matrix;
}
template<typename Matrix2D>
auto rotate_2d_matrix_clockwise(Matrix2D const & original_matrix) -> Matrix2D
{
int const M = original_matrix[0].size();
int const N = original_matrix.size();
Matrix2D rotated_matrix;
rotated_matrix.resize(M);
for (auto x = 0; x < M; ++x) {
rotated_matrix[x].resize(N);
}
rotate_2d_matrix_clockwise_impl(original_matrix, rotated_matrix, M, N);
return rotated_matrix;
}
int main()
{
std::array<std::array<int, 4>, 3> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
std::cout << "\nBefore Rotation :\n";
print_matrix(data);
std::cout << "\nAfter 2nd Clockwise Rotation :\n";
print_matrix(rotate_2d_matrix_clockwise(rotate_2d_matrix_clockwise(data)));
std::vector<std::vector<char>> data2 = { {'a', 'b', 'c', 'd'}, {'e', 'f', 'g', 'h'}, {'i', 'j', 'k', 'l'}};
std::cout << "Before Rotation :\n";
print_matrix(data2);
std::cout << "\nAfter Clockwise Rotation :\n";
print_matrix(rotate_2d_matrix_clockwise(data2));
return 0;
}
If I got you right, and all you want is to print the matrix 90 degrees clockwise, try this code:
for (int colNo = 0; colNo < vec[0].size(); colNo++)
{
for (int i = vec.size() - 1; i >= 0; i--)
{
const auto& row = vec[i];
int colVal = row.at(colNo);
cout << colVal;
}
cout << endl;
}
This mean that while I am sorting the v2 in the nonincreasing order, the v1 should looks like this:
Vectors look as following.
v1 = {0, 5, 5, 2, 10};
v2 = {0 ,2, 6, 20, 5};
The output:
v1 = {2, 5, 10, 5, 0};
v2 = {20, 6, 5, 2, 0};
I was trying to solve that problem mixing std::sort and lambdas.
That is why I have read several questions about std::sort, there were no answers which solve the problem similar to mine so that is why I am asking.
I am especially interested in the answers which contains it's usage or other features of C++11 and C++14.
It is not a question like:
"I completely do not know what to do."
I know how to achieve the output using C++98, but I am wondering if there is an more efficient and prettier way to do it.
Big thanks for your help :)
You could zip, sort, and unzip.
#include <iostream>
#include <vector>
#include <algorithm>
//converts two vectors into vector of pairs
template <typename T, typename U>
auto zip(T t, U u) {
std::vector<std::pair<typename T::value_type,typename U::value_type>> pairs;
for (size_t i = 0; i < t.size(); ++i){
pairs.emplace_back(u[i],t[i]);
}
return pairs;
}
//converts a vector of pairs, back into two two vectors
template <typename T, typename U, typename V>
void unzip(V pairs, T & t, U & u) {
for (auto const& it: pairs){
u.emplace_back(it.first);
t.emplace_back(it.second);
}
}
int main(){
//vectors
std::vector<int> v1 = {0, 5, 5, 2, 10};
std::vector<int> v2 = {0 ,2, 6, 20, 5};
//zip vectors
auto pairs = zip(v1,v2);
//sort them
std::sort(pairs.begin(),pairs.end(),std::greater<>());
//unzip them
v1.clear();
v2.clear();
unzip(pairs,v1,v2);
//print
std::cout << '\n';
for (auto i: v1) std::cout << i << ' ';
std::cout << '\n';
for (auto i: v2) std::cout << i << ' ';
std::cout << '\n';
}
Well, I don't if this is would be efficient, or not, but this demonstrates how to do this with std::generate, std::sort, and std::transform, with some extra seasoning from mutable lambdas, and iterators.
#include <algorithm>
#include <iostream>
int main()
{
std::vector<int> v1={0, 5, 5, 2, 10},
v2 = {0, 2, 6, 20, 5};
std::vector<int> index;
index.resize(5);
std::generate(index.begin(), index.end(),
[n=0]
()
mutable
{
return n++;
});
std::sort(index.begin(), index.end(),
[&]
(auto &a, auto &b)
{
return v2[b] < v2[a];
});
std::vector<int> v1_out, v2_out;
std::transform(index.begin(), index.end(),
std::back_insert_iterator<std::vector<int>>(v1_out),
[&]
(auto a)
{
return v1[a];
});
std::transform(index.begin(), index.end(),
std::back_insert_iterator<std::vector<int>>(v2_out),
[&]
(auto a)
{
return v2[a];
});
for (auto n: v1_out)
std::cout << n << ' ';
std::cout << std::endl;
for (auto n: v2_out)
std::cout << n << ' ';
std::cout << std::endl;
}
Big thanks for all your answers. I found an easy way to achieve the same effect and the idea comes from this answer.
1.
Firstly I used all code from answer which I linked, so it is:
template <typename T>
vector<size_t> sort_indexes(const vector<T> &v)
{
// initialize original index locations
vector<size_t> idx(v.size());
for (size_t i = 0; i != idx.size(); ++i) idx[i] = i;
// sort indexes based on comparing values in v
sort(idx.begin(), idx.end(),
[&v](size_t i1, size_t i2)
{
return v[i1] >= v[i2];
});
return idx;
}
2.Then I did a function which will sort first and a second vector using the third one.To achieve it I had to create temporary vectors one for a first vector, one for a second, and two for the last one.
Why two?
Because I have to remember the sorted indexes of the third vector and I need a one temporary to which I will be pushing elements of the original third vector according to sorted indexes.
void SortByIndexes(vector<int>& Pi,vector<int> &Wi,vector<int>& PidivWi)
{
vector<int> Pitemp, Witemp, PidivWitemp,SortedIndexes;
for (auto i : sort_indexes(PidivWi))
{
SortedIndexes.push_back(i);
}
for (auto i : SortedIndexes)
{
Pitemp.push_back(Pi[i]);
Witemp.push_back(Wi[i]);
PidivWitemp.push_back(PidivWi[i]);
}
swap(Pi, Pitemp);
swap(Wi, Witemp);
swap(PidivWi,PidivWitemp);
}
3. After sorting just swap sorted vectors with original ones.
Done.
Thank you all guys.