Use one template for one-dimensional array and three-dimensional array - c++

I have a template that uses a three-dimensional array to find the maximum. The crux of the problem is that this template must find the maximum in a one-dimensional array. We add a question with a char variable, if question = '1' = three-dimensional, if 2, then one-dimensional.
l need use template T2 for one dimensional and thee dimensional it depends of question(char)
T2 maxShablon2(T2 ***arr, const int n) {
int max = arr[0][0][0];
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
for (int k = 0; k < n; ++k) {
if (arr[i][j][k] > max) {
max = arr[i][j][k];
}
}
}
}
cout << " Our max: " << max;
}

template<std::size_t N>
using count = std::integral_constant<std::size_t, N>;
template<class T>
constexpr T maxOver( count<0> unused, T t, int n ) {
return t;
}
template<std::size_t depth, class Ptr>
constexpr auto maxOver( count<depth>, Ptr* t, int n ) {
auto max = maxOver( count<depth-1>{}, t[0], n );
for (int i = 1; i < n; ++i) {
auto candidate = maxOver( count<depth-1>{}, t[i], n );
if (candidate > max)
max = candidate;
}
return max;
}
now,
constexpr int arr[100] = {1,2,3,0};
constexpr int arr_max = maxOver( count<1>{}, arr, 100 );
static_assert(arr_max == 3);
constexpr int arr3[5][5][5] = {{{1,-1,7},{2,3,4}},{{3},{4,5,9}}};
constexpr int arr3_max = maxOver( count<3>{}, arr3, 5 );
static_assert(arr3_max == 9);
passes (Live example).

Related

C++ - response is int%

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';
}

Generalized Outer Product - function accepts lambda

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';
}
}

compact form of many for loop in C++

I have a piece of code as follows, and the number of for loops is determined by n which is known at compile time. Each for loop iterates over the values 0 and 1. Currently, my code looks something like this
for(int in=0;in<2;in++){
for(int in_1=0;in_1<2;in_1++){
for(int in_2=0;in_2<2;in_2++){
// ... n times
for(int i2=0;i2<2;i2++){
for(int i1=0;i1<2;i1++){
d[in][in_1][in_2]...[i2][i1] =updown(in)+updown(in_1)+...+updown(i1);
}
}
// ...
}
}
}
Now my question is whether one can write it in a more compact form.
The n bits in_k can be interpreted as the representation of one integer less than 2^n.
This allows easily to work with a 1-D array (vector) d[.].
In practice, an interger j corresponds to
j = in[0] + 2*in[1] + ... + 2^n-1*in[n-1]
Moreover, a direct implementation is O(NlogN). (N = 2^n)
A recursive solution is possible, for example using
f(val, n) = updown(val%2) + f(val/2, n-1) and f(val, 0) = 0.
This would correspond to a O(N) complexity, at the condition to introduce memoization, not implemented here.
Result:
0 : 0
1 : 1
2 : 1
3 : 2
4 : 1
5 : 2
6 : 2
7 : 3
8 : 1
9 : 2
10 : 2
11 : 3
12 : 2
13 : 3
14 : 3
15 : 4
#include <iostream>
#include <vector>
int up_down (int b) {
if (b) return 1;
return 0;
}
int f(int val, int n) {
if (n < 0) return 0;
return up_down (val%2) + f(val/2, n-1);
}
int main() {
const int n = 4;
int size = 1;
for (int i = 0; i < n; ++i) size *= 2;
std::vector<int> d(size, 0);
for (int i = 0; i < size; ++i) {
d[i] = f(i, n);
}
for (int i = 0; i < size; ++i) {
std::cout << i << " : " << d[i] << '\n';
}
return 0;
}
As mentioned above, the recursive approach allows a O(N) complexity, at the condition to implement memoization.
Another possibility is to use a simple iterative approach, in order to get this O(N) complexity.
(here N represents to total number of data)
#include <iostream>
#include <vector>
int up_down (int b) {
if (b) return 1;
return 0;
}
int main() {
const int n = 4;
int size = 1;
for (int i = 0; i < n; ++i) size *= 2;
std::vector<int> d(size, 0);
int size_block = 1;
for (int i = 0; i < n; ++i) {
for (int j = size_block-1; j >= 0; --j) {
d[2*j+1] = d[j] + up_down(1);
d[2*j] = d[j] + up_down(0);
}
size_block *= 2;
}
for (int i = 0; i < size; ++i) {
std::cout << i << " : " << d[i] << '\n';
}
return 0;
}
You can refactor your code slightly like this:
for(int in=0;in<2;in++) {
auto& dn = d[in];
auto updown_n = updown(in);
for(int in_1=0;in_1<2;in_1++) {
// dn_1 == d[in][in_1]
auto& dn_1 = dn[in_1];
// updown_n_1 == updown(in)+updown(in_1)
auto updown_n_1 = updown_n + updown(in_1);
for(int in_2=0;in_2<2;in_2++) {
// dn_2 == d[in][in_1][in_2]
auto& dn_2 = dn_1[in_2];
// updown_n_2 == updown(in)+updown(in_1)+updown(in_2)
auto updown_n_2 = updown_n_1 + updown(in_2);
.
.
.
for(int i2=0;i2<2;i1++) {
// d2 == d[in][in_1][in_2]...[i2]
auto& d2 = d3[i2];
// updown_2 = updown(in)+updown(in_1)+updown(in_2)+...+updown(i2)
auto updown_2 = updown_3 + updown(i2);
for(int i1=0;i1<2;i1++) {
// d1 == d[in][in_1][in_2]...[i2][i1]
auto& d1 = d2[i1];
// updown_1 = updown(in)+updown(in_1)+updown(in_2)+...+updown(i2)+updown(i1)
auto updown_1 = updown_2 + updown(i1);
// d[in][in_1][in_2]...[i2][i1] = updown(in)+updown(in_1)+...+updown(i1);
d1 = updown_1;
}
}
}
}
}
And make this into a recursive function now:
template<std::size_t N, typename T>
void loop(T& d) {
for (int i = 0; i < 2; ++i) {
loop<N-1>(d[i], updown(i));
}
}
template<std::size_t N, typename T, typename U>
typename std::enable_if<N != 0>::type loop(T& d, U updown_result) {
for (int i = 0; i < 2; ++i) {
loop<N-1>(d[i], updown_result + updown(i));
}
}
template<std::size_t N, typename T, typename U>
typename std::enable_if<N == 0>::type loop(T& d, U updown_result) {
d = updown_result;
}
If your type is int d[2][2][2]...[2][2]; or int*****... d;, you can also stop when the type isn't an array or pointer instead of manually specifying N (or change for whatever the type of d[0][0][0]...[0][0] is)
Here's a version that does that with a recursive lambda:
auto loop = [](auto& self, auto& d, auto updown_result) -> void {
using d_t = typename std::remove_cv<typename std::remove_reference<decltype(d)>::type>::type;
if constexpr (!std::is_array<d_t>::value && !std::is_pointer<d_t>::value) {
// Last level of nesting
d = updown_result;
} else {
for (int i = 0; i < 2; ++i) {
self(self, d[i], updown_result + updown(i));
}
}
};
for (int i = 0; i < 2; ++i) {
loop(loop, d[i], updown(i));
}
I am assuming that it is a multi-dimensional matrix. You may have to solve it mathematically first and then write the respective equations in the program.

Avoiding code duplication when the only difference is loop control statements (with the same statements in loop bodies)?

In my solution code for project euler problem 11, I got the following functions. Max_consecutive_prod is a class which calculates the max product of consecutive input()ed numbers, generalised from problem 8. The six functions calculate max product in different series of different directions and start from different edges of the grid.
The only difference in these functions is indexes in for statements, how to elimilate the obvious duplication? The situation here is somehow the opposite to the typical application of template method pattern: the operation is identical but the control framework is different, is there another design pattern for this?
Edit: all the modifications specified in comments are to the (two) for statements, and the loop body in each function is identical to the first.
template <size_t size> unsigned process_row(const unsigned (&grid)[size][size])
{
unsigned prodMax = 0;
for (int i = 0; i < size; ++i)
{
Max_consecutive_prod mcp;
for (int j = 0; j < size; ++j)
{
mcp.input(grid[i][j]);
}
if (mcp.result() > prodMax)
{
prodMax = mcp.result();
}
}
return prodMax;
}
// exchange i, j in process_row
template <size_t size> unsigned process_col(const unsigned (&grid)[size][size])
{
// ...
}
template <size_t size> unsigned process_diag_lower(const unsigned (&grid)[size][size])
{
unsigned prodMax = 0;
for (int init = 0; init < size; ++init)
{
Max_consecutive_prod mcp;
for (int i = init, j = 0; i < size && j < size; ++i, ++j)
// ...
// ...
}
return prodMax;
}
// exchange i, j in process_diag_lower
template <size_t size> unsigned process_diag_upper(const unsigned (&grid)[size][size])
{
// ...
}
// flip j in process_diag_lower
template <size_t size> unsigned process_rev_diag_lower(const unsigned (&grid)[size][size])
{
unsigned prodMax = 0;
for (int init = 0; init < size; ++init)
{
Max_consecutive_prod mcp;
for (int i = init, j = size-1; i < size && j >= 0; ++i, --j)
// ...
// ...
}
return prodMax;
}
// change ++j in process_diag_upper to --j
template <size_t size> unsigned process_rev_diag_upper(const unsigned (&grid)[size][size])
{
unsigned prodMax = 0;
for (int init = 0; init < size; ++init)
{
Max_consecutive_prod mcp;
for (int j = init, i = 0; j >=0 && i < size; ++i, --j)
// ...
// ...
}
return prodMax;
}
Based on random-hacker's code, which shows the real commonality and variability in control flows of the six function, I wrote my version and made the code more self-explaining and C++ idiomatic, using a stragegy class, defining local variables to clarify the code and improve effiency. I define a non-template version of process(), to avoid binary code bloat when instantizing for different size (see 'Effective C++', Item 44).
If you still get confused, please read random-hacker's answer for explanation. :)
namespace Grid_search
{
enum Step { neg = -1, nul, pos };
enum Index_t { i, j };
struct Strategy
{
Step direction[2];
Index_t varOuter;
};
const size_t typeCount = 6;
const Strategy strategy[typeCount] = { {{pos, nul}, i}, {{nul, pos}, j}, {{pos, pos}, i}, {{pos, pos}, j}, {{pos, neg}, i}, {{pos, neg}, j} };
};
template <size_t size> inline unsigned process(const Grid_search::Strategy& strategy, const unsigned (&grid)[size][size])
{
return process(strategy, reinterpret_cast<const unsigned*>(&grid), size);
}
unsigned process(const Grid_search::Strategy& strategy, const unsigned* grid, size_t size)
{
using namespace Grid_search;
const Index_t varOuter = strategy.varOuter, varInner = static_cast<Index_t>(!varOuter);
const Step di = strategy.direction[i], dj = strategy.direction[j];
const unsigned initInner = strategy.direction[varInner] == pos ? 0 : size -1;
unsigned prodMax = 0;
unsigned index[2];
unsigned &indexI = index[i], &indexJ = index[j];
for (unsigned initOuter = 0; initOuter < size; ++initOuter)
{
Max_consecutive_prod mcp;
for (index[varOuter] = initOuter, index[varInner] = initInner;
0 <= indexI && indexI < size && 0 <= indexJ && indexJ < size;
indexI += di, indexJ += dj)
{
mcp.input(grid[indexI*size + indexJ]);
if (mcp.result() > prodMax)
{
prodMax = mcp.result();
}
}
}
return prodMax;
}
int main()
{
static const size_t N = 20;
unsigned grid[N][N];
std::ifstream input("d:/pro11.txt");
for (int count = 0; input >> grid[count/N][count%N]; ++count)
{
}
unsigned prodMax = 0;
for (int i = 0; i < Grid_search::typeCount; ++i)
{
unsigned prod = process(Grid_search::strategy[i], grid);
if (prod > prodMax)
{
prodMax = prod;
}
}
}
Although I think what you already have will be fine after sticking the inner loop code blocks in an ordinary function as suggested by Adam Burry and Tony D, if you want you can combine the loops, using tables to encode the possible directions to move in. The trick is to use an array p[2] instead of separate i and j, to enable the question of which index is varied in the outer loop to be driven by a table. Then the only tricky thing is making sure that the other index, which will be varied in the inner loop, needs to start at its maximum value (instead of 0) iff it will decrement at each step:
enum indices { I, J }; // Can just use 0 and 1 if you want
template <size_t size> unsigned process(const unsigned (&grid)[size][size]) {
static int d[][2] = { {1, 0}, {0, 1}, {1, 1}, {1, -1}, {1, 1}, {1, -1} };
static int w[] = { J, I, J, J, I, I };
unsigned prodMax = 0; // Note: not 1
for (int k = 0; k < sizeof d / sizeof d[0]; ++k) { // For each direction
for (int init = 0; init < size; ++init) {
Max_consecutive_prod mcp;
int p[2]; // p[I] is like i, p[J] is like j
for (p[w[k]] = init, p[!w[k]] = (d[k][!w[k]] == -1 ? size - 1 : 0);
min(p[I], p[J]) >= 0 && max(p[I], p[J]) < size;
p[I] += d[k][I], p[J] += d[k][J])
{
mcp.input(grid[p[I]][p[J]]);
prodMax = max(prodMax, mcp.result());
}
}
}
return prodMax;
}
You could create an enum for the different states and then pass it into the function. You would then create an if statement that would set the values based on the passed value.
Your process_row() has a bug: from the example in the link, zero entries are allowed in the matrix, so if a row begins with e.g.
x y z 0 ...
and any of x, xy or xyz is larger than all other 4-element products on the rest of that row and on any other row in the matrix, it will incorrectly report that the this is the largest 4-element product. (I'm assuming here that Max_consecutive_prod calculates a rolling product of the last 4 elements provided with input()).
Unless your Max_consecutive_prod is unusually aware of how it is being called, you will also get erroneous results "wrapping" from the end of one row to the next, and from one process_...() call to the next.
Suppose you flattened the grid so that it was just 400 numbers in a row, reading left to right and then top to bottom. The topmost row would consist of the first 20 numbers (that is, indices 0, ..., 19); the second rwo of the next 20 numbers, etc. In general, row i (starting from 0) would correspond to indices i*20, i*20 + 1, i*20 + 2, ..., i*20 + 19.
Now, what about columns? The leftmost column starts at position 0, just like the topmost row. It's next element at position 20 (the first element in the second row), and then 40, and... So it's not hard to see that the indices for column j are j, j + 20, j + 40, ..., j + 19*20.
Diagonals are not much different. Try it on paper (grid-ruled paper is good for this sort of thing.)
One more hint: Does it make a difference if you find the product of four elements, multiplying left-to-right, than the same four elements multiplying right-to-left?
First, the Context object approach - this just packages the arguments to the support functions mentioned in my comment on your question... it's about as useful as the problem was significant ;-].
struct Context
{
unsigned& proxMax;
int i, j;
Max_consecutive_prod mcp;
Context(unsigned& prodMax) : prodMax(prodMax) { }
};
template <size_t size> unsigned process_diag_lower(const unsigned (&grid)[size][size])
{
unsigned prodMax = 0;
for (int init = 0; init < size; ++init)
{
Context context(prodMax);
for (context.i = init, context.j = 0; context.i < size && context.j < size; ++context.i, ++context.j)
loop_ij(context);
loop_outer(context);
}
return prodMax;
}
Visitor pattern. Now, I said in my comment "you don't show us enough loop bodies to see the common requirements", and haven't seen anything since, so on the basis of the one body I've seen - namely:
template <size_t size> unsigned process_row(const unsigned (&grid)[size][size])
{
unsigned prodMax = 0;
for (int i = 0; i < size; ++i)
{
Max_consecutive_prod mcp;
for (int j = 0; j < size; ++j)
{
mcp.input(grid[i][j]);
}
if (mcp.result() > prodMax)
{
prodMax = mcp.result();
}
}
return prodMax;
}
The above can be split:
template <size_t size, template Visitor>
unsigned visit_row(const unsigned (&grid)[size][size], Visitor& visitor)
{
for (int i = 0; i < size; ++i)
{
for (int j = 0; j < size; ++j)
visitor.inner{grid[i][j]);
visitor.outer();
}
return visitor.result();
}
struct Visitor
{
unsigned prodMax;
Max_consecutive_prod mcp;
Visitor() : prodMax(0) { }
void inner(unsigned n) { mcp.input(n); }
void outer()
{
if (mcp.result() > prodMax) prodMax = mcp.result();
mcp = Max_consecutive_prod(); // reset for next time...
}
unsigned result() const { return prodMax; }
};
This way, the same Visitor class can be combined with your various grid-element iteration routines.

Iterating over subsets of any size

I can iterate over the subsets of size 1
for( int a = 0; a < size; a++ ) {
or subsets of size 2
for( int a1 = 0; a1 < size; a1++ ) {
for( int a2 = a1+1; a2 < size; a2++ ) {
or 3
for( int a1 = 0; a1 < size; a1++ ) {
for( int a2 = a1+1; a2 < size; a2++ ) {
for( int a3 = a2+1; a3 < size; a3++ ) {
But how to do this for subsets of size n?
This does the job, based on an answer by Adam Rosenfield
void iterate(int *a, int i, int size, int n)
{
int start = 0;
if( i > 0 ) start = a[i-1]+1;
for(a[i] = start; a[i] < n; a[i]++) {
if(i == n-1) {
// a is the array of indices of size n
for( int k = 0; k < size; k++ ) {
printf("%d ",a[k]);
}
printf("\n");
}
else
iterate(a, i+1, size, n);
}
}
You can use recursion:
void iterate(int *a, int i, int size, int n)
{
for(a[i] = 0; a[i] < size; a[i]++)
{
if(i == n-1)
DoStuff(a, n); // a is the array of indices of size n
else
iterate(a, i+1, size, n);
}
}
...
// Equivalent to 4 nested for loops
int a[4];
iterate(a, 0, size, 4);
You likely could do this with some recursion.
Here is something I used for a similar problem. It does not use recursion; rather, it uses a vector of indexes.
#include <vector>
template<class T>
class MultiForVar {
std::vector<T> _begin, _end, _vars;
inline int dim(){return _vars.size();}
public:
MultiForVar(std::vector<T> begin, std::vector<T> end) : _begin(begin), _end(end), _vars(_begin)
{
assert(begin.size() == end.size() and "Starting and ending vector<T> not the same size!" );
}
MultiForVar& operator ++()
{
++_vars[dim()-1];
for(int d = dim()-1; d > 0; --d)
{
if( _vars[d] >= _end[d] )
{
_vars[d] = _begin[d];
++_vars[d-1];
}
}
return *this;
}
bool done()
{
/*for(int d = 0; d < dim(); ++d)
if( _vars[d] < _end[d] )
return false;
return true;*/
return (_vars[0] >= _end[0]);
}
T operator[](int d)
{
return _vars.at(d);
}
int numDimensions(){
return dim();
}
std::vector<T>& getRaw(){
return _vars;
}
};
If I understand what you're asking correctly, another way to do it is to use bit-wise operators:
for(int i = 0; i < 1<<size; i++) {
for(int j = 0; j < size; j++) {
if(i & 1<<j) printf("%d ", a[j]);
}
printf("\n");
}
You need something the constructs the powerset of the original set. It's been a while since I've written that, but the psuedocode looks like
Powerset(a, size)
{
if(size == 0) return emptyset
subseta = Powerset(a, size-1) // Powerset of everything except last element
subsetb = appendToAll(a[size-1], subseta) // appends the last element to every set in subseta
return union(subseta, subsetb)
}