1

I have a 1D array of length size * size, representing a square field of values. My goal is to rotate the array in place (previous question). I currently have issues getting the correct index in the inner rings. What is the mistake in my algorithm?

This is my code, skip below for an explanation & examples.

Code (Rust 1.41.0)

fn rotate_square_slice<T>(slice: &mut [T], size: usize) {
    for r in 0..(size + 1) / 2 {
        // current ring side length
        let l = size - 1 - r;
        for i in r..l {             
            let a = size *    r    +  r+i ;
            let b = size *  (r+i)  +  l-r ;
            let c = size *  (l-r)  + l-r-i;
            let d = size * (l-r-i) +   r  ;

            slice.swap(a, b);
            slice.swap(a, c);
            slice.swap(a, d);
        }
    }
}

Explanation

array = [A, B, C, D, E,
         A, B, C, D, E,
         A, B, C, D, E,
         A, B, C, D, E,
         A, B, C, D, E]

ring 0:         |   symmetries:
                |
    A B C D E   |   A . . . E     . B . . .     . . C . .
    A . . . E   |   . . . . .     . . . . E     . . . . .        
    A . . . E   |   . . . . .  +  . . . . .  +  A . . . E  +  etc...
    A . . . E   |   . . . . .     A . . . .     . . . . .
    A B C D E   |   A . . . E     . . . D .     . . C . . 

ring 1:         |   symmetries:
                |
    . . . . .   |   . . . . .   . . . . . 
    . B C D .   |   . B . D .   . . C . .
    . B . D .   |   . . . . .   . B . D .
    . B C D .   |   . B . D .   . . C . .
    . . . . .   |   . . . . .   . . . . . 

Example Iteration Step

   0 1 2 3 4

0  . a . . .
1  . . . . b
2  . . . . .
3  d . . . .
4  . . . c .

size = 5    |    position(a) = (  r  ,  r+i ) = (0, 1)
r    = 0    |    position(b) = ( r+i ,  l-r ) = (1, 4)
l    = 4    |    position(c) = ( l-r , l-r-i) = (4, 3)
i    = 1    |    position(d) = (l-r-i,   r  ) = (3, 0)

Example Output

Using 1D-indexing on a 5*5 "square" array, here are the desired and current output of all tuples of indices (a, b, c, d):

desired output   | current output   | parameters
                 |                  | r  l  i
( 0,  4, 24, 20) | ( 0,  4, 24, 20) | 0  4  0
( 1,  9, 23, 15) | ( 1,  9, 23, 15) | 0  4  1
( 2, 14, 22, 10) | ( 2, 14, 22, 10) | 0  4  2
( 3, 19, 21,  5) | ( 2, 14, 22, 10) | 0  4  3
                 |                  |
( 6,  8, 18, 16) | ( 7, 12, 11,  6) | 1  3  1 <- mistake
( 7, 13, 17, 11) | ( 8, 17, 10,  1) | 1  3  2 <- mistake
                 |                  |

I hope the ASCII illustrations help to demonstrate what I want. If clarification is needed, please let me know.

Orphevs
  • 125
  • 7
  • My guess is that I'm making a mistake with how I handle the ring side length l. I can't put my finger on it, though. – Orphevs Feb 28 '20 at 01:32
  • It looks like you're substracting `r` too many times: once when you compute `l` and then again each time you use it (as `l-r`). Replace all your `l-r` with straight `l`. – Jmb Feb 28 '20 at 08:05
  • @Jmb That might be it. The first tuple's first value is wrong as well though (7 instead of 6) and the first value's formula doesn't include ‘l‘ . Do you have any ideas there? – Orphevs Feb 28 '20 at 08:58
  • Yes: `i` starts at `r`, not 0. So you should also replace all `i+r` with just plain `i` (and `-i-r` with `-i`). – Jmb Feb 28 '20 at 13:18
  • @Jmb thank you. Total blocker right there. My mistake was counting i from 0 mentally. Your version almost works, except for the case c, if I'm not mistaken. I'm not near a computer right now, but will try to implement the changes later. Thanks a ton, really! – Orphevs Feb 28 '20 at 14:44
  • On a second thought, using l = size - 1 - r doesn't hold for the center if size%2==1. Example for size=5, center on ring 2: l = 5-1-2 = 2, while the center ring consists of just one element. – Orphevs Feb 28 '20 at 15:16

1 Answers1

0

The issue was caused by using l in the calculations at all.

An element's position is directly related to the ring, size & index, but not to how many unique indices there are on the current ring (l). This was my mistake in the original code.

As mentioned by @Gene in the comments, rotating the i'ths row left by i steps and the j'ths column down by j steps, one could achieve a similar result. I still believe that the approach I present below has its merits, as it can be easily extended to allow for arbitrary condition checks on what tuples of elements to rotate.

enum Rotation {
    Clockwise,
    Counterclockwise,
}

fn rotate_square_slice<T>(slice: &mut [T], s: usize, rotation: Rotation) {
    // iterate ringwise, from outer to inner
    // skip center when size % 2 == 1
    for r in 0..s / 2 { 
        // for all unique indices under rotational symmetry ...
        for i in 0..s - (2 * r) - 1{
            // ... get their 4 corresponding positions ...
            let a = s * (   r   ) +   r+i   ;
            let b = s * (  r+i  ) +  s-r-1  ;
            let c = s * ( s-r-1 ) + s-r-i-1 ;
            let d = s * (s-r-i-1) +    r    ;

            //... and swap them in the correct direction.
            match rotation {
                Rotation::Clockwise => {
                    slice.swap(a, b);
                    slice.swap(a, c);
                    slice.swap(a, d);
                },
                Rotation::Counterclockwise => {
                    slice.swap(a, b);
                    slice.swap(c, d);
                    slice.swap(b, d);
                }
            }
        }
    }
}

Huge thanks to @Jmb! Didn't see the wood for the trees.

Because of the slice's linear layout, it is easy to just rotate a subslice of some Vec by using chunks(). Neat!

Community
  • 1
  • 1
Orphevs
  • 125
  • 7
  • fwiw, here's an interesting fact. If you rotate the i'th row left by i positions (0-based numbering), then rotate the j'th column downward by j positions, and finally repeat the first operation (i'th rotated left by i positions), the result is the original matrix rotated by 90 degrees. – Gene Feb 29 '20 at 04:12
  • @Gene I know, but I also believe it's a nice challenge to work out how to do it this way. If you have any performance concerns, I opened a question on Code Review SE: https://codereview.stackexchange.com/q/238145/162096 – Orphevs Feb 29 '20 at 11:53