问题
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