1
$\begingroup$

Some time ago I invented an algorithm for in-place matrix transposition using rotations.

A matrix of size N×M is represented as a one-dimensional array of size N*M, which is also the future storage for the corresponding transposed M×N matrix.

The algorithm moves an element to its proper position in the representation for the transposed matrix rotating the sub-array of the array 1 element right, starting at the correct and ending at the current position of the element.

Let matrix be the array of size N*M, where N and M are the number of rows and columns in the original matrix (and the number of columns and rows in the transposed). Array indices are from 0 to N*M-1.

Let dst be the proper position for the element in the array for the transposed matrix, src — its current position, src_inc is the value addet to src to point to the "current position" of the next element to be moved. The algorithm starts with dst = 1, src_inc = M and src = dst + src_inc - 1 (which is equal to M) (The element at the 0-th position is already in its right place). After the first rotation, we increment dst by 1 and src by src_inc (which is M) until dst points to an element which should not be moved. This happens when (dst + 1) % N is 0. In this case we skip the element (incrementing dst again), decrease src_inc by one and restore the initial relation between src and dstsrc = dst + src_inc - 1.

Let me, instead of writing a pseudo-code, show the C++ code and add a state of the array before each rotation (plus the final result)

#include <algorithm>

template<typename T>
void inplace_transpose_using_rotations(T* matrix, int N, int M)
{
    int dst = 1;
    int src_inc = M;
    int src = dst + src_inc - 1;

    while (src < N*M && dst < src)
    {
        std::rotate(matrix + dst, matrix + src, matrix + src + 1); // rotate right for 1 position
        if ((dst + 1) % N != 0) {
            dst += 1;
            src += src_inc;
        } else {
            dst += 2;
            src_inc -= 1;
            src = dst + src_inc - 1;
        }
    }
}

(https://wandbox.org/permlink/XAWE1Fn64SxvEvth)

Original 3×5 matrix:

11 12 13 14 15 
21 22 23 24 25 
31 32 33 34 35 

The trace (the first row is indices, the matrix is represented linearly, dst and src to the right are shown before the rotation, the result of the rotation is on the next line):

00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 
11 12 13 14 15 21 22 23 24 25 31 32 33 34 35 | dst = 01, src = 05
11 21 12 13 14 15 22 23 24 25 31 32 33 34 35 | dst = 02, src = 10
11 21 31 12 13 14 15 22 23 24 25 32 33 34 35 | dst = 04, src = 07
11 21 31 12 22 13 14 15 23 24 25 32 33 34 35 | dst = 05, src = 11
11 21 31 12 22 32 13 14 15 23 24 25 33 34 35 | dst = 07, src = 09
11 21 31 12 22 32 13 23 14 15 24 25 33 34 35 | dst = 08, src = 12
11 21 31 12 22 32 13 23 33 14 15 24 25 34 35 | dst = 10, src = 11
11 21 31 12 22 32 13 23 33 14 24 15 25 34 35 | dst = 11, src = 13
11 21 31 12 22 32 13 23 33 14 24 34 15 25 35 | final array

(the code producing the trace https://wandbox.org/permlink/3nhlwYoSz5GnWuUP)

And transposed matrix (reinterpretation of the array as a representation for a 5×3 matrix):

11 21 31 
12 22 32 
13 23 33 
14 24 34 
15 25 35 

The algorithm is not optimal, it does N(N-1)/2 M(M-1)/2 swaps (assuming the rotation is implemented as a chain of swaps), but it is short and seems to work.

However, I couldn't prove why it should work. I was just curious to try to transpose a matrix using rotations instead of chain of swaps following cycles and discovered that there is a relatively simple law how to select the next sub-array for rotation. Is this algorithm published anywhere?

$\endgroup$
  • 2
    $\begingroup$ Please get rid of the source code and replace it with ideas, pseudo code and (if possible) arguments of correctness. See here and here for related meta discussions. $\endgroup$ – dkaeae Jul 22 at 14:00
  • 1
    $\begingroup$ @dkaeae I've left the code, but added some explanation and a trace $\endgroup$ – user107819 Jul 22 at 15:25
1
$\begingroup$

After the array is rotated with dst == N - 1 (so the condition (dst + 1) % N == 0 is about to fire), the tail of the array starting from index N looks like a representation of a N×(M-1) matrix with the values of the original matrix without the first column:

11 21 31 12 13 14 15 22 23 24 25 32 33 34 35
         ^ starting from here

looks like a representation of the

12 13 14 15
22 23 24 25
32 33 34 35

matrix

which immediately suggests how to simplify the algorithm:

template<typename T>
void inplace_transpose_using_rotations(T* matrix, int N, int M)
{
    if (N == 1 || M == 1)
        return;

    for (int dst = 1; dst != N; dst++)
        std::rotate(matrix + dst, matrix + dst * M, matrix + dst * M + 1);

    inplace_transpose_using_rotations(matrix + N, N, M - 1);
}

This explains how the algorithm works: rotations move first elements of each row into their proper position in the transposed matrix and at the same time shift the other elements to the right forming a representation of a smaller N×(M-1) matrix in the right side of the array. Then this smaller matrix is transposed, until we get a N×1 matrix.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy