-1

Inspired by arr2D[i][j] and arr1D[i * k + j] after reading this post and the comments under it, I would like to know an algorithm that can change the dimensions of any array.

Let me try to formalize it:

Input:

  1. An M-dimensional container A

  2. The dimensions D of the container (size)

  3. Target dimension N

Output:

If N > 0 then return an N-dimensional container B with the same contents as A in order otherwise return an error code.

Note:

You may choose any optimal size for the N-dimensional container.

Edit:

I don't need any fully working code. I'm asking if there's any algorithm that does this?

Ardent Coder
  • 3,777
  • 9
  • 27
  • 53
  • 1
    I think that would be similar to convert number representation from one base to another. Only difference that base can be different for every dimension. For example you can treat decimal number 1234 as an index in 4 dimensional array where each dimension has size 10. – Slava Jan 29 '20 at 19:00
  • 1
    Any code to show? – Jarod42 Jan 29 '20 at 19:05
  • 1
    Show what you have tried so far. – User_67128 Jan 29 '20 at 19:08
  • @Jarod42 It's bedtime and the question suddenly popped into my mind. I may attempt it tomorrow and show you some code as an answer to this question if I could do it. – Ardent Coder Jan 29 '20 at 19:09
  • 2
    For practical purpose just a templated wrapper to linear `std::vector` that provides X dimensional access should be enough, Only question would be what to do when underltying vector has to be different size. – Slava Jan 29 '20 at 19:10
  • Are you writing about serialization/deserialization where the size of data stays the same or you want some kind of projection/mapping changing geometrical dimensionality (of vertexes or voxels)? For the first you simply encode M-dim into 1D array and convert back to N-dim. For the latter see [4D rendering techniques](https://stackoverflow.com/a/44970550/2521214) – Spektre Jan 30 '20 at 08:49
  • @Spektre I'm not doing anything serious with it. The question came into my mind after having a small discussion on N-dimensional arrays in a comment section of this site. – Ardent Coder Jan 30 '20 at 10:40
  • 1
    @ArdentCoder I added answer with the conversion in C++ ... – Spektre Jan 31 '20 at 09:39

4 Answers4

1

Converts [100] to [10][10]:

#include <iostream>

typedef int (*parray)[10];

parray foo (int *in) {
    return (parray)in;
}

int main()
{
    int in[100];
    in[55] = 42;
    int (*out)[10] = foo(in);
    std::cout << out[5][5] << std::endl;

    return 0;
}
stark
  • 12,615
  • 3
  • 33
  • 50
  • 1
    Breaks at least strict aliasing rule, and so UB. – Jarod42 Jan 29 '20 at 19:21
  • I still couldn't understand this, maybe that typedef and function. Could you please explain it? – Ardent Coder Feb 01 '20 at 09:20
  • 1
    As @Jarod points out, you don't want to do this except as an exercise on SO. It is remapping 100 ints from 100x1 to 10x10 using casting. Arrays are just address arithmetic under the hood. – stark Feb 01 '20 at 16:54
  • Oh I see @stark Thanks. Do you have any idea why the question has downvotes? – Ardent Coder Feb 01 '20 at 17:09
  • 1
    No idea. I'm kind of surprised I didn't get any. – stark Feb 01 '20 at 22:30
  • @stark Because "This question does not show any research effort"... I was simply curious to know if there existed any such algorithm after having had a [discussion](https://stackoverflow.com/questions/59970668/generate-arbitrarily-nested-vectors-in-c#comment106058648_59970668) about it in a comment section of this very site. It wasn't like a homework problem, and I think many comments have been removed from there. – Ardent Coder May 15 '20 at 13:22
1

Your original M-dimensional container is A.

We want to create a new N-dimensional container B that will hold all content of A.

First we have to figure out a mapping where we can easily find the same element in A and in B.

Let's use some examples to deduce how the mapping could be:

(1) M = 2, N = 1

A: a * b    B: c
we can set the dimension c to be a * b, thus we have
A[i][j] = B[i * c + j]

(2) M = 3, N = 1

A: a * b * c    B: d
d = a * b * c
A[i][j][k] = B[(i * b * c) + (j * c) + k]

(3) M = 3, N = 2

A: a * b * c    B: d * e
d = a, e = b * c
A[i][j][k] = B[i][j * c + k]

(4) M = 4, N = 1

A: a * b * c * d    B: e
e = a * b * c * d
A[i][j][k][l] = B[(i * b * c * d) + (j * c * d) + (k * d) + l]

(5) M = 5, N = 4

A: a * b * c * d * e    B: u * v * w * x
u = a, v = b, w = c, x = d * e
A[i][j][k][l][m] = B[i][j][k][(l * e) + m]

(6) M = 5, N = 2

A: a * b * c * d * e    B: f * g
f = a, g = b * c * d * e
A[i][j][k][l][m] = B[i][(j * c * d * e) + (k * d * e) + (l * e) + m]

If A has M dimensions a1, a2, ..., aM and B has N dimensions b1, b2, ..., bN, we can say that:

if M > N, then for all 0 < i < N, bi = ai and bN = aN * aN+1 * ... * aM.

This way we know how to create B and the size of each dimension of it.

With the mapping function shown in the examples, you can easily convert any M-dimension matrix to a N-dimension matrix.

If M < N, you can do the same thing but in opposite direction.

Daniel
  • 7,357
  • 7
  • 32
  • 84
1

As you don't need a code, let me explain how you could do it with templates.

Say you have an D-dimensional array of size n_{0},...,n_{d-1} you can always remove one dimension by merging two of them multiplying the sizes. Examples: a[5][4][3] contains 60 elements, thus b[20][3] or c[5][12] (for simple cases, because you may even construct d[15][4] and any permutations) can easily contains the same elements as a, the mapping of indices is also such obvious...

Now using C++ doing it it much more tricky but you will need: variadic templates and template metaprogramming.

Variadic template to define an array type of any dimension you like, and template metaprogramming to define an operator to map a D-dimensional array to a N-dimensional one. (I may say that it will not be easy, but a very -hard- good exercise in template metaprogramming).

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
1

So you just want to reformat your matrices while no data is changed. As I hinted in my comment the simplest is to use 1D array mid step for converting from M to N dimensions.

The other answers here are on the same track but are lacking the whole math ... they have just examples up to some small dimension without universal equation so here it is:

To convert between A[A0][A1]...[A(M-1)] and X[A0*A1*...*A(M-1)] where A0,A1,...A(M-1) are the dimension sizes of your container (resolution) simply do this:

// M-D -> 1D
x = a0
   +a1*A0
   +a2*A0*A1
   ...
   +a(M-1)*A0*A1*...*A(M-2);

// 1D -> M-D   
q=x;
a0 = q%A0; q/=A0;
a1 = q%A1; q/=A1;
a2 = q%A2; q/=A2;
...
a(M-1) = q%A(M-1); q/=A(M-1);

where a0,a1,...a(M-1) and x are the indexes in the arrays.

You actually do not need to convert the M-D array into 1D and then back to N-D its enough to just convert the indexes so:

for (a0=0;a0<A0;a0++)
 for (a1=0;a1<A1;a1++)
  ...
   for (a(M-1)=0;a(M-1)<A(M-1);a(M-1)++)
      {
      // M-D -> 1D
      x = a0
         +a1*A0
         +a2*A0*A1
         ...
         +a(M-1)*A0*A1*...*A(M-2);
      // 1D -> N-D   
      q=x;
      b0 = q%B0; q/=B0;
      b1 = q%B1; q/=B1;
      b2 = q%B2; q/=B2;
      ...
      b(N-1) = q%B(N-1); q/=B(N-1);
      // copy A -> B
      B[b0][b1]...[b(N-1)] = A[A0][A1]...[A(M-1)];
      }

Do not forget that the sizes must be:

A0*A1*...*A(M-1) <= B0*B1*...*B(N-1)

otherwise you will access array out of its bounds as the data in A will not fit into B.

In case you got dynamic dimensionality you can use:

Spektre
  • 49,595
  • 11
  • 110
  • 380