Rotational Symmetry Indexing in a 1D “Square” Array

大兔子大兔子 提交于 2020-03-03 07:31:05

问题


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.


回答1:


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!



来源:https://stackoverflow.com/questions/60443977/rotational-symmetry-indexing-in-a-1d-square-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!