I'm trying to make a function which will make generalized outer product of two arrays in a way that creates dynamically allocated 2D array (matrix).
The types of the first two parameters must be identical to the types of the next two parameters, but the types of the first two parameters do not have to be identical to the types of the next two parameters (eg the first two parameters can be pointers to deck of integers). This also implies that the elements of these two arrays do not necessarily have to be of the same type. As for the function 𝑓, it should be a lambda function. The only limitation is that it must be able to receive elements of these two arrays as parameters. The type of value returned by the function 𝑓 can be any, and this type will be elements of the matrix.
EXAMPLE
5 2 8
1 3 6 2
Function f(x,y)=x+2y
7 11 17 9
4 8 14 6
10 14 20 12
#include <iostream>
#include <vector>
/*I don't know how to make Generalized_Outer_Product accept lambda function*/
template < typename iterator_tip1, typename iterator_tip2, typename tip >
auto Generalized_Outer_Product(iterator_tip1 start_first, iterator_tip1 after_end_first,
iterator_tip2 start_second, iterator_tip2 after_end_second, tip f(tip)) {
using type_of_object_first = typename std::decay < decltype( * start_first) > ::type;
using type_of_object_second = typename std::decay < decltype( * start_second) > ::type;
int n1 = std::distance(start_first, after_end_first);
int n2 = std::distance(start_second, after_end_second);
type_of_object_first ** mat = nullptr;
mat = new int * [n1];
for (int i = 0; i < n1; i++)
mat[i] = nullptr;
try {
for (int i = 0; i < n1; i++)
mat[i] = new type_of_object_first[n2];
for (int i = 0; i < n1; i++) {
for (int j = 0; j < n2; j++) {
mat[i][j] = f( * start_first, * start_second);
start_second++;
}
start_first++;
start_second -= n2;
}
} catch (...) {
for (int i = 0; i < n1; i++)
delete[] mat[i];
delete[] mat;
throw std::range_error("Not enough memory");
}
return mat;
}
int main() {
int n1, n2, x;
std::cin >> n1;
std::vector < int > a, b, c;
for (int i = 0; i < n1; i++) {
std::cin >> x;
a.push_back(x);
}
std::cin >> n2;
for (int i = 0; i < n2; i++) {
std::cin >> x;
b.push_back(x);
}
try {
std::cout << "Generalized Outer Product f(x,y)=x+2y:" << std::endl;
int ** mat = nullptr;
mat = Generalized_Outer_Product(a.begin(), a.end(), b.begin(), b.end(), [](int x, int y) {
return x + 2 * y;
});
for (int i = 0; i < n1; i++) {
for (int j = 0; j < n2; j++)
std::cout << mat[i][j] << " ";
std::cout << std::endl;
}
for (int i = 0; i < n1; i++)
delete[] mat[i];
delete[] mat;
} catch (std::range_error e) {
std::cout << e.what();
}
return 0;
}
Could you help me to accept lambda function on the correct way?
TL DR
Just leave the type of the parameter open and let the compiler deal with invalid parameter types; use decltype to determine the result element type:
template<class Iterator1, class Iterator2, class Function>
auto GeneralizedOuterProduct(Iterator1 const start1, Iterator1 const end1, Iterator2 const start2, Iterator2 const end2, Function&& f)
{
using ResultType = decltype(f(*start1, *start2));
...
}
Additional Advice + Full Code
First create yourself a type that manages the lifetime of the data. This simplifies the exception handling. If done correctly you can simply let your exceptions go unhandled in the GeneralizedProduct function without having to worry about memory leaks.
The following class simply uses a single std::vector to store the data of one row after the other. The index operator uses the fact that the vector iterator allows us to write iter[index] to get a reference to the element index elements after the iterator position.
template<class T>
struct MultiplicationResult
{
public:
using ValueType = T;
using IndexOperatorElement = std::vector<ValueType>::iterator;
using IndexOperatorElementConst = std::vector<ValueType>::const_iterator;
MultiplicationResult(size_t dimension1, std::vector<T>&& data)
: m_dimension1(dimension1)
{
if (data.size() % dimension1 != 0)
{
throw std::runtime_error("invalid data: the number of data elements is not divisible by dimension1");
}
m_dimension2 = data.size() / dimension1;
m_data = std::move(data);
}
IndexOperatorElement operator[](size_t index)
{
return m_data.begin() + index * m_dimension2;
}
IndexOperatorElementConst operator[](size_t index) const noexcept
{
return m_data.cbegin() + index * m_dimension2;
}
size_t Dimension1() const noexcept
{
return m_dimension1;
}
size_t Dimension2() const noexcept
{
return m_dimension2;
}
private:
std::vector<T> m_data;
size_t m_dimension1;
size_t m_dimension2;
};
Now all for the GeneralizedProduct function to do is to determine the result type and create the vector that will be used as data for MultiplicationResult.
Note that we do not restrict the type of the function passed here in any way. The compilation will result in errors, if the call operator of the argument passed in cannot be called given the the dereferenced iterators or if the result type isn't something that can be used as template parameter of std::vector. Note that lambdas are objects with a call operator and therefore not assignable to result_type(argument_type1, argument_type2). (I consider this approach more convenient than using std::function<result_type(argument_type1, argument_type2)>:
template<class Iterator1, class Iterator2, class Function>
auto GeneralizedOuterProduct(Iterator1 const start1, Iterator1 const end1, Iterator2 const start2, Iterator2 const end2, Function&& f)
we determine the result type calling the call operator simply using decltype:
using ResultType = decltype(f(*start1, *start2));
template<class Iterator1, class Iterator2, class Function>
auto GeneralizedOuterProduct(Iterator1 const start1, Iterator1 const end1, Iterator2 const start2, Iterator2 const end2, Function&& f)
{
using ResultType = decltype(f(*start1, *start2));
size_t const n1 = std::distance(start1, end1);
// if an exception happens, data will ensure the elements already allocated get destroyed
std::vector<ResultType> data;
data.reserve(n1 * std::distance(start2, end2));
for (Iterator1 pos1 = start1; pos1 != end1; ++pos1)
{
for (Iterator2 pos2 = start2; pos2 != end2; ++pos2)
{
data.push_back(f(*pos1, *pos2));
}
}
return MultiplicationResult(n1, std::move(data)); // C++17 CTAD deduces the template arguments here
}
Example usage (without exception handling:
int main() {
std::vector<int> a{ 5, 2, 8 };
std::vector<int> b{ 1, 3, 6, 2 };
auto result = GeneralizedOuterProduct(a.begin(), a.end(), b.begin(), b.end(), [](int x, int y) { return x + 2* y; });
size_t const d1 = result.Dimension1();
size_t const d2 = result.Dimension2();
for (size_t i = 0; i != d1; ++i)
{
auto innerArray = result[i];
for (size_t j = 0; j != d2; ++j)
{
std::cout << std::setw(5) << innerArray[j];
}
std::cout << '\n';
}
}
Related
I decide 2D Dinamic Coding on C++, i'm decide task about count of ways to bottom-right field in table, and my program return %. Why?
Program:
#include <iostream>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
int arr[n][m];
for (int i = 0; i < n; i++)
arr[i][0] = 1;
for (int i = 0; i < m; i++)
arr[0][i] = 1;
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++)
arr[i][j] = arr[i-1][j] + arr[i][j-1];
}
cout << arr[n-1][m-1];
}
I would like answer
Request:
1 10
Response:
1
Your program has undefined behavior for any other sizes than n = 1 and m = 1 because you leave the non-standard VLA (variable length array) arr's positions outside arr[0][0] uninitialized and later read from those positions. If you want to continue using these non-standard VLA:s, you need to initialize them after constructing them. Example:
#include <cstring> // std::memset
// ...
int arr[n][m];
std::memset(arr, 0, sizeof arr); // zero out the memory
// ...
Another approach that would both make it initialized and be compliant with standard C++ would be to use std::vectors instead:
#include <vector>
// ...
std::vector<std::vector<int>> arr(n, std::vector<int>(m));
// ...
A slightly more cumbersome approach is to store the data in a 1D vector inside a class and provide methods of accessing the data as if it was stored in a 2D matrix. A class letting you store arbitrary number of dimensions could look something like below:
#include <utility>
#include <vector>
template <class T, size_t Dim> // number of dimensions as a template parameter
class matrix {
public:
template <class... Args>
matrix(size_t s, Args&&... sizes) // sizes of all dimensions
: m_data(s * (... * sizes)), // allocate the total amount of data
m_sizes{s, static_cast<size_t>(sizes)...}, // store sizes
m_muls{static_cast<size_t>(sizes)..., 1} // and multipliers
{
static_assert(sizeof...(Args) + 1 == Dim);
for (size_t i = Dim - 1; i--;)
m_muls[i] *= m_muls[i + 1]; // calculate dimensional multipliers
}
template <size_t D> size_t size() const { return m_sizes[D]; }
size_t size(size_t D) const { return m_sizes[D]; }
// access the data using (y,z) instead of [y][x]
template <class... Args>
T& operator()(Args&&... indices) {
static_assert(sizeof...(Args) == Dim);
return op_impl(std::make_index_sequence<Dim>{}, indices...);
}
private:
template <std::size_t... I, class... Args>
T& op_impl(std::index_sequence<I...>, Args&&... indices) {
return m_data[(... + (indices * m_muls[I]))];
}
std::vector<T> m_data;
size_t m_sizes[Dim];
size_t m_muls[Dim];
};
With such a wrapper, you'd only need to change the implementation slightly:
#include <iostream>
int main() {
int n, m;
if(!(std::cin >> n >> m && n > 0 && m > 0)) return 1;
matrix<int, 2> arr(n, m);
for (int i = 0; i < arr.size<0>(); i++)
arr(i, 0) = 1;
for (int i = 0; i < arr.size<1>(); i++)
arr(0, i) = 1;
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++)
arr(i, j) = arr(i - 1, j) + arr(i, j - 1);
}
std::cout << arr(n - 1, m - 1) << '\n';
}
The algorithm is simple and straight-forward:
Keep break the n dimensional vector into n-1 dimensional constituent vectors, until you have access to the basic-datatype objects. Their addition is simple and well-defined.
Here is my implementation using templates and operator+:
#include <iostream>
#include <vector>
template<class T>
std::vector<T> operator+ (const std::vector<T>& v1, const std::vector<T>& v2)
{
std::vector<T> output;
unsigned len = v1.size();
output.resize(len);
for (int i = 0; i < len; ++i)
output[i] = v1[i] + v2[i];
return output;
}
int main ()
{
using namespace std;
vector<std::vector<int>> x = {{8,9,0},{5,6,7}};
vector<std::vector<int>> y = {{1,1,1},{1,1,1}};
auto result = x + y; // Yeah, just like that !
// Being Lazy, instead of implementing multi-dimensional vector printing...
for (int i = 0; i < result.size(); ++i)
{
for (int j=0; j<result[i].size(); ++j)
cout << result.at(i).at(j) << " ";
cout << "\n";
}
return 0;
}
But suppose you are not allowed to use operator+.
You have to design an interface as some function Add().
But I'm unable to do so!! Here's my attempt:
#include <iostream>
#include <vector>
// Hoping to handle vectors...
template<class T>
std::vector<T> Add (const std::vector<T>& v1, const std::vector<T>& v2)
{
std::vector<T> output;
unsigned len = v1.size();
output.resize(len);
for (int i = 0; i < len; ++i)
output[i] = Add(v1[i], v2[i]);
return output;
}
// Hoping to handle basic-datatypes...
template<class T>
T Add (const T& v1, const T& v2)
{
T output;
unsigned len = v1.size();
output.resize(len);
for (int i = 0; i < len; ++i)
output[i] = v1[i] + v2[i];
return output;
}
int main ()
{
using namespace std;
vector<std::vector<int>> x = {{8,9,0},{5,6,7}};
vector<std::vector<int>> y = {{1,1,1},{1,1,1}};
auto result = Add(x, y); // I wish ! But not happening !
// Being Lazy, instead of implementing multi-dimensional vector printing...
for (int i = 0; i < result.size(); ++i)
{
for (int j=0; j<result[i].size(); ++j)
cout << result.at(i).at(j) << " ";
cout << "\n";
}
return 0;
}
Is it even possible?
You must provide a version for Add() that works on basic data types
// Handle basic-datatypes
template<class T>
T Add(const T& x1, const T& x2)
{
return x1 + x2;
}
// Specialisation for vectors
template<class T>
std::vector<T> Add(std::vector<T> const& v1, std::vector<T> const& v2)
{
assert(v1.size()==v2.size());
std::vector<T> result;
result.reserve(v1.size());
for(size_t i=0; i!=v1.size(); ++i)
result.emplace_back(Add(v1[i],v2[i])); // possibly recursive
return result;
}
Instead of this:
// Hoping to handle basic-datatypes...
template<class T>
T Add (const T& v1, const T& v2)
{
T output;
unsigned len = v1.size();
output.resize(len);
for (int i = 0; i < len; ++i)
output[i] = v1[i] + v2[i];
return output;
}
It has to be:
// Hoping to handle basic-datatypes...
template<class T>
T Add (const T& v1, const T& v2)
{
return v1 + v2;
}
Also the one which handles basic-datatypes must come before the one which handles vectors.
I have a Matrix template class and I need a function to set it's elements with variable number of args.
I should be able to call it like this:
aghMatrix<string> matrix;
matrix.setItems(2, 3, "torzmiae", "jestdnaci", "tablickore", "wyrazobed", "oelmntai", "rozmiaecy");
Where first integer is rows number, second is columns and rest (R * C) arguments are elements that I should put into matrix.
It should work with any data types, not only primitive ones.
For now, my function looks like this:
template<typename T>
template<typename... ARGS>
void aghMatrix<T>::setItems(const int rows, const int cols, ARGS... args) {
array<T, sizeof...(args)>unpacked_args {args...};
int row = 0;
int col = 0;
for (T arg : unpacked_args)
{
this->matrixPtr[row][col] = arg;
col++;
if (col == this->cols) {
row++;
col = 0;
}
}
return;
}
I assumed my matrix object is able to hold all elements. It does compile with many warnings about casting everything to unsigned int, but the program doesn't work anyway (it freezes on start).
Class declaration:
template<typename T>
class aghMatrix {
public:
[...]
template<typename... ARGS> void setItems(const int rows, const int cols, ARGS... args);
[...]
private:
T **matrixPtr;
int rows;
int cols;
void createMatrix(const int row, const int col);
bool checkRowCol(const int row, const int col) const;
};
Github project
EDIT: Oops! I just noticed you said "non recursive," so I presume the following pattern doesn't work for you. I'll still leave it hanging here for now, but I have provided also a non recursive solution below (which is based on va_list and hence only works with POD types)
If I understand correctly what you want to do, then you probably want the recursive variadic argument unpacking pattern; something like this seems to do the trick...
#include <iostream>
using namespace std;
// Helper for build_matrix, taking zero variadic arguments.
// This serves as the termination in the recursive unpacking of the args.
template<typename T>
void build_matrix_helper(T**, size_t, size_t, size_t, size_t) { return; }
// Helper for build_matrix, taking at least one variadic argument.
template <typename T, typename ...ARGS>
void build_matrix_helper(T** matrix, size_t curr_row, size_t curr_col,
size_t row, size_t col, const T& first, ARGS...rest) {
if (curr_col < col) {
matrix[curr_row][curr_col] = first;
++curr_col;
return build_matrix_helper<T>(matrix, curr_row, curr_col, row, col, rest...);
}
else {
++curr_row;
curr_col = 0;
return build_matrix_helper<T>(matrix, curr_row, curr_col, row, col, first, rest...);
}
return;
}
// Bare bones implementation.
template<typename T, typename ...ARGS>
T **build_matrix(size_t row, size_t col, ARGS...elements) {
T **new_mat = new T*[row];
for (size_t j = 0; j < row; ++j)
new_mat[j] = new T[col];
build_matrix_helper<T>(new_mat, 0, 0, row, col, elements...);
return new_mat;
}
int main() {
int **nm = build_matrix<int>(2, 3, 1, 2, 3, 4, 5, 6);
for (size_t i = 0; i < 2; ++i) {
cout << "[" << i + 1 << "]: ";
for (size_t j = 0; j < 3; ++j)
cout << nm[i][j] << " ";
cout << endl;
}
delete[] nm;
return 0;
}
In general, you want to avoid any direct manipulation of memory as much as possible. Also avoid as much as possible any casting voodoo unless you absolutely need it (which also ties in with direct memory manipulation).
Anyway, can use a non recursive solution below, using std::va_list.
NOTE Since this uses va_list, it does not work with non POD types.
#include <iostream>
#include <cstdarg>
using namespace std;
template <typename T>
T **build_matrix(size_t row, size_t col, ...) {
va_list args;
T **matrix = new T*[row];
va_start(args, col);
for (size_t i = 0; i < row; ++i) {
matrix[i] = new T[col];
for (size_t j = 0; j < col; ++j)
matrix[i][j] = va_arg(args, T);
}
va_end(args);
return matrix;
}
int main() {
int **nm = build_matrix<int>(2, 3, 1, 2, 3, 4, 5, 6);
for (size_t i = 0; i < 2; ++i) {
cout << "[" << i + 1 << "]: ";
for (size_t j = 0; j < 3; ++j)
cout << nm[i][j] << " ";
cout << endl;
}
delete[] nm;
return 0;
}
EDIT Initializer lists
As has been suggested in the comments to your OP, it is better to use initializer lists. I know this isn't what you asked for originally, but maybe it's worth considering:
#include <iostream>
#include <stdexcept>
using namespace std;
template <typename T>
T **build_matrix(size_t row, size_t col, initializer_list<T> il) {
if (il.size() != row*col)
throw out_of_range("Number of elements does not match matrix dimensions!");
size_t curr_row = 0;
size_t curr_col = 0;
T **nm = new T*[row];
nm[0] = new T[col];
for (T elm : il) {
if (curr_col == col) {
++curr_row;
nm[curr_row] = new T[col];
curr_col = 0;
}
nm[curr_row][curr_col] = elm;
++curr_col;
}
return nm;
}
int main() {
int **nm = build_matrix<int>(2, 3, {1, 2, 3, 4, 5, 6});
for (size_t i = 0; i < 2; ++i) {
cout << "[" << i + 1 << "]: ";
for (size_t j = 0; j < 3; ++j)
cout << nm[i][j] << " ";
cout << endl;
}
delete[] nm;
return 0;
}
I am working on a N dimensional grid.
I would like to generate nested loops depending on any dimension (2D, 3D, 4D, etc...).
How can I do that in an elegant and fast way ? Below a simple illustration of my problem.
I am writing in C++ but I think this kind of question can be useful for other languages.
I need to know the indices (i,j,k...) in my do stuff part.
Edit : lower_bound and upper_bound represents the indexes in the grid so they are always positive.
#include <vector>
int main()
{
// Dimension here is 3D
std::vector<size_t> lower_bound({4,2,1});
std::vector<size_t> upper_bound({16,47,9});
for (size_t i = lower_bound[0]; i < upper_bound[0]; i ++)
for (size_t j = lower_bound[1]; j < upper_bound[1]; j ++)
for (size_t k = lower_bound[2]; k < upper_bound[2]; k ++)
// for (size_t l = lower_bound[3]; l < upper_bound[3]; l ++)
// ...
{
// Do stuff such as
grid({i,j,k}) = 2 * i + 3 *j - 4 * k;
// where grid size is the total number of vertices
}
}
Following may help:
bool increment(
std::vector<int>& v,
const std::vector<int>& lower,
const std::vector<int>& upper)
{
assert(v.size() == lower.size());
assert(v.size() == upper.size());
for (auto i = v.size(); i-- != 0; ) {
++v[i];
if (v[i] != upper[i]) {
return true;
}
v[i] = lower[i];
}
return false;
}
And use it that way:
int main() {
const std::vector<int> lower_bound({4,2,1});
const std::vector<int> upper_bound({6,7,4});
std::vector<int> current = lower_bound;
do {
std::copy(current.begin(), current.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
} while (increment(current, lower_bound, upper_bound));
}
Live demo
An iterative approach could look like this:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> lower_bound({-4, -5, -6});
std::vector<int> upper_bound({ 6, 7, 4});
auto increase_counters = [&](std::vector<int> &c) {
for(std::size_t i = 0; i < c.size(); ++i) {
// This bit could be made to look prettier if the indices are counted the
// other way around. Not that it really matters.
int &ctr = c .rbegin()[i];
int top = upper_bound.rbegin()[i];
int bottom = lower_bound.rbegin()[i];
// count up the innermost counter
if(ctr + 1 < top) {
++ctr;
return;
}
// if it flows over the upper bound, wrap around and continue with
// the next.
ctr = bottom;
}
// end condition. If we end up here, loop's over.
c = upper_bound;
};
for(std::vector<int> counters = lower_bound; counters != upper_bound; increase_counters(counters)) {
for(int i : counters) {
std::cout << i << ", ";
}
std::cout << "\n";
}
}
...although whether this or a recursive approach is more elegant rather depends on the use case.
#include <iostream>
#include <vector>
template <typename Func>
void process(const std::vector<int>& lower, const std::vector<int>& upper, Func f)
{
std::vector<int> temp;
process(lower, upper, f, 0, temp);
}
template <typename Func>
void process(const std::vector<int>& lower, const std::vector<int>& upper, Func f,
int index, std::vector<int>& current)
{
if (index == lower.size())
{
f(current);
return;
}
for (int i = lower[index]; i < upper[index]; ++i)
{
current.push_back(i);
process(lower, upper, f, index + 1, current);
current.pop_back();
}
}
int main()
{
// Dimension here is 3D
std::vector<int> lower_bound({-4, -5, 6});
std::vector<int> upper_bound({6, 7, 4});
// Replace the lambda below with whatever code you want to process
// the resulting permutations.
process(lower_bound, upper_bound, [](const std::vector<int>& values)
{
for (std::vector<int>::const_iterator it = values.begin(); it != values.end(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
});
}
Probably some typos an whatnot, but I'd flatten the whole range.
This is based on the idea that the range can be described as
x_0 + d_0*(x_1+d_1*(x_2+d_2....)
So we can roll our own that way
std::vector<int> lower_bound{-4,-5,6};
std::vector<int> upper_bound{6,7,4};
//ranges
std::vector<int> ranges;
for (size_t i = 0; i < lower_bound.size(); i++) {
ranges.push_back(upper_bound[i]-lower_bound[i]);
}
for (int idx = 0; idx < numel; idx++) {
//if you don't need the actual indicies, you're done
//extract indexes
int idx2 = idx;
std::vector<int> indexes;
for (int i = 0; i < ranges.size(); i++) {
indexes.push_back(idx2%ranges[i]-lower_bound[i]);
idx2 = idx2/ranges[i];
}
//do stuff
grid[idx] = 2 * indexes[0] + 3 *indexes[1] - 4 * indexes[2];
}
Edit: to be more generic:
template <typename D>
void multi_for(const std::vector<int>& lower_bound, const std::vector<int> upper_bound, D d) {
std::vector<int> ranges;
for (size_t i = 0; i < lower_bound.size(); i++) {
ranges.push_back(upper_bound[i]-lower_bound[i]);
}
size_t numel = std::accumulate(ranges.begin(), ranges.end(), std::multiplies<int,int>{});
for (int idx = 0; idx < numel; idx++) {
//if you don't need the actual indicies, you're done
//extract indexes
int idx2 = idx;
std::vector<int> indexes;
for (int i = 0; i < ranges.size(); i++) {
indexes.push_back(idx2%ranges[i]-lower_bound[i]);
idx2 = idx2/ranges[i];
}
//do stuff
d(idx,indexes);
}
}
//main
size_t* grid;//initialize to whateer
std::vector<int> lower_bound{-4,-5,6};
std::vector<int> upper_bound{6,7,4};
auto do_stuff = [grid](size_t idx, const std::vector<int> indexes) {
grid[idx] = 2 * indexes[0] + 3 *indexes[1] - 4 * indexes[2];
};
multi_for(lower_bound,upper_bound,do_stuff);
A recursive function may help you achieve what you want.
void Recursive( int comp )
{
if(comp == dimension)
{
// Do stuff
}
else
{
for (int e = lower_bound[comp]; e < upper_bound[comp]; e++)
Recursive(comp+1);
}
}
Some additions may be necessary in the function signature if you need to know the current indices (i,j,k,...) in your "Do Stuff" section.
This is a clean way to have access to these indices
void Recursive( int comp, int dimension )
{
static std::vector<int> indices;
if( comp == 0 ) // initialize indices
{
indices.clear();
indices.resize(dimension, 0);
}
if(comp == dimension -1)
{
// Do stuff
}
else
{
int& e = indices[comp];
for (e = lower_bound[comp]; e < upper_bound[comp]; e++)
Recursive(comp+1);
}
}
This is however not usable along multiple threads, due to the shared static vector.
How do you create a multidimensional array (matrix) whose dimensions are determined at runtime.
The best way seems to be to take a vector of dimensions for construction and also a vector of offsets to access individual elements
This will also allow using initializer lists:
This should take a matrix of types determined at compile time, so templates make sense
C++11 features should be used as appropriate, bonus points for lambdas
Example usage:
int main(int, char **)
{
static const std::size_t d1{2};
static const std::size_t d2{3};
static const std::size_t d3{4};
multi_vec<std::size_t> q({d1,d2,d3});
for (std::size_t i1=0; i1 < d1; ++i1)
for (std::size_t i2=0; i2 < d2; ++i2)
for (std::size_t i3=0; i3 < d3; ++i3)
q[{i1,i2,i3}]=foo(i1,i2,i3);
for (std::size_t i1=0; i1 < d1; ++i1)
for (std::size_t i2=0; i2 < d2; ++i2)
for (std::size_t i3=0; i3 < d3; ++i3)
std::cout << "{"
<< i1 << ","
<< i2 << ","
<< i3 << "}=> "
<< foo(i1,i2,i3) << "=="
<< q[{i1,i2,i3}]
<< ((foo(i1,i2,i3) == q[{i1,i2,i3}])
? " good" : " ERROR")
<< std::endl;
};
This is actually a good place to use lambdas with std::for_each and unique_ptr for memory management:
template <class T>
class multi_vec
{
public:
using param=std::vector<size_t>;
explicit multi_vec(const param& dimensions)
: dim{dimensions}, prod {1}
{
std::for_each(dim.begin(), dim.end(), [this] (std::size_t val)
{
mult.emplace_back(prod);
prod *= val;
} );
ptr.reset(new T[prod]);
}
std::size_t capacity() const { return prod; }
// undefined if elements in lookup != elemenets in dim
// undefined if any element in lookup
// is greater than or equal to corresponding dim element
T& operator[](const param& lookup)
{
return ptr[get_offset(lookup)];
}
const T operator[](const param& lookup) const
{
return ptr[get_offset(lookup)];
}
private:
std::size_t get_offset(const param& lookup) const
{
std::size_t offset=0;
auto mit=mult.begin();
std::for_each(lookup.begin(), lookup.end(), [&offset, &mit] (std::size_t val)
{
offset+=*mit * val;
++mit;
} );
return offset;
}
param dim;
param mult;
std::size_t prod;
std::unique_ptr<T[]> ptr;
};
This uses a single dimensional array for actual storage and caches multipliers for reuse
Since a normal multidimension array e.g. x[2][3][4] does not bounds check, going out of bounds is deemed undefined instead of constantly checking values that are probably valid. In the same way diminsionality is not checked for lookups, if required, a static member can be added to check if a parameter is valid, in terms of bounds or dimensionality.
As answered here https://stackoverflow.com/a/19725907/2684539 :
#include <cstddef>
#include <vector>
template<typename T>
class MultiArray
{
public:
explicit MultiArray(const std::vector<size_t>& dimensions) :
dimensions(dimensions),
values(computeTotalSize(dimensions))
{
assert(!dimensions.empty());
assert(!values.empty());
}
const T& get(const std::vector<size_t>& indexes) const
{
return values[computeIndex(indexes)];
}
T& get(const std::vector<size_t>& indexes)
{
return values[computeIndex(indexes)];
}
size_t computeIndex(const std::vector<size_t>& indexes) const
{
assert(indexes.size() == dimensions.size());
size_t index = 0;
size_t mul = 1;
for (size_t i = 0; i != dimensions.size(); ++i) {
assert(indexes[i] < dimensions[i]);
index += indexes[i] * mul;
mul *= dimensions[i];
}
assert(index < values.size());
return index;
}
std::vector<size_t> computeIndexes(size_t index) const
{
assert(index < values.size());
std::vector<size_t> res(dimensions.size());
size_t mul = values.size();
for (size_t i = dimensions.size(); i != 0; --i) {
mul /= dimensions[i - 1];
res[i - 1] = index / mul;
assert(res[i - 1] < dimensions[i - 1]);
index -= res[i - 1] * mul;
}
return res;
}
private:
size_t computeTotalSize(const std::vector<size_t>& dimensions) const
{
size_t totalSize = 1;
for (auto i : dimensions) {
totalSize *= i;
}
return totalSize;
}
private:
std::vector<size_t> dimensions;
std::vector<T> values;
};
int main()
{
MultiArray<int> m({3, 2, 4});
m.get({0, 0, 3}) = 42;
m.get({2, 1, 3}) = 42;
for (size_t i = 0; i != 24; ++i) {
assert(m.computeIndex(m.computeIndexes(i)) == i);
}
return 0;
}
You use C. Honestly, C's dynamic arrays are superior to anything C++ offers. Here is the code for a 3D array:
//Allocation, the type needs some effort to understand,
//but once you do understand it, this is easy:
int width = 7, height = 8, depth = 9;
int (*array)[height][width] = malloc(depth*sizeof(*array));
//Filling, completely natural...
for(int z = 0; z < depth; z++) {
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
array[z][y][x] = 42;
}
}
}
//Deallocation, trivial...
free(array);
A 3D array is nothing more or less than a 1D array of 2D arrays (which are 1D arrays of 1D arrays in turn), so you declare a pointer *array to a 2D array of integers int (...)[heigh][width], and allocate space for depth such elements malloc(depth*sizeof(*array)). This is fully analogous to creating a 1D array with int *array = malloc(length*sizeof(*array));. The rest is array-pointer-decay magic.
This works is C, because C allows array types to contain dynamic sizes (since C99). It's like defining two constants with the declared sizes at the point of the declaration. C++, on the other hand, still insists on array sizes being compile-time constants, and thus fails to allow you multi-dimensional arrays of dynamic size.