Rotating a 2D Matrix
Octave, 210 bytes
function M=F(M,R);f=@(z)[-z/2:-1 !eye(!!mod(z,2)) 1:z/2];t=angle(f([x y]=size(M))'+f(y)*i);B=!!M;B(2:x-1,2:y-1)=0;d=bwdist(B,'ch');[~,I]=sortrows([d(:) t(:)]);for k=0:max(d(:));M(h)=shift(M(h=I(d(I)==k)),R);end
Try it on Octave Online!
Ungolfed version:
function M=F(M,R)
[x y]=size(M);
f=@(z)[-z/2:-1 !eye(!!mod(z,2)) 1:z/2];
t=angle(f(x)'+f(y)*i);
B=!!M;
B(2:x-1,2:y-1)=0;
d=bwdist(B,'chessboard');
[~,I]=sortrows([d(:) t(:)]);
for k=0:max(d(:))
M(h)=shift(M(h=I(d(I)==k)),R);
end
end
R=2;
M=randi(10,4,7)
F(M,R)
Explanation:
f=@(z)[-z/2:-1 !eye(!!mod(z,2)) 1:z/2];
A function that gets a number and generates a range that is ordered and centered
for input 4 (even) generates -2 -1 1 2
for input 5(odd) generates -2.5 -1.5 0 1 2
only it should be ordered and centered
f(x)'+f(y)*i
a complex matrix generated from ranges
(-2,-2.5) (-2,-1.5) (-2,0) (-2,1) (-2,2)
(-1,-2.5) (-1,-1.5) (-1,0) (-1,1) (-1,2)
(1,-2.5) (1,-1.5) (1,0) (1,1) (1,2)
(2,-2.5) (2,-1.5) (2,0) (2,1) (2,2)
t=angle(f(x)'+f(y)*i);
Convert rectangular to polar coordinates and return angles so for each ring angles are sorted counteclockwise
-2.25 -2.50 3.14 2.68 2.36
-1.95 -2.16 3.14 2.36 2.03
-1.19 -0.98 0.00 0.79 1.11
-0.90 -0.64 0.00 0.46 0.79
B=!!M;
B(2:x-1,2:y-1)=0;
The following matrix generated
1 1 1 1 1
1 0 0 0 1
1 0 0 0 1
1 1 1 1 1
d=bwdist(B,'chessboard');
Computes the distance transform of B using chessboard distance to generate ring indices
0 0 0 0 0
0 1 1 1 0
0 1 1 1 0
0 0 0 0 0
for a 6 * 7 matrix we will have the following matrix
0 0 0 0 0 0 0
0 1 1 1 1 1 0
0 1 2 2 2 1 0
0 1 2 2 2 1 0
0 1 1 1 1 1 0
0 0 0 0 0 0 0
[~,I]=sortrows([d(:) t(:)]);
lexicographic sort first based on ring index and then by order of angle(indices of sorted elements returned)
for k=0:max(d(:))
M(h)=shift(M(h=I(d(I)==k)),R);
end
and finally circular shift each ring.
Jelly, 39 38 36 35 bytes
ḢṚ;Ḣ€;Ṫ;Ṫ€Ṛ$,-ṙ\;"ß¹¡
FJ©ṁ¹Çy®ịFṁµ¡
Try it online!
Python 3, 292 288 bytes
_=eval
a=input().split()
b,a=int(a[0]),_("".join(a[1:]))[::-1]
c=len(a)
d=len(a[0])
e=range
f="int((i>=j and i+j<c-1)|2*(i>=c/2and i+d>j+c)|3*(i<c/2and i+j<d))"
l=[-1,1,0,0],[0,0,1,-1]
g=lambda x:[[x[i+l[0][_(f)]][j+l[1][_(f)]]for j in e(d)]for i in e(c)]
print(_("g("*b+"a"+")"*b)[::-1])
Takes input with newlines removed, but leaving a space after the number of increments to rotate it by.
Explanation:
Instead of modeling the matrix as a series of concentric rings per the OP's suggestion, one can instead divide it into four regions where the elements travel either up, down, right, or left during a single rotation. This is the purpose of the long eval-ed string f
: to determine which region each i,j
combination falls into. Then, the result of that is looked up twice in l
, giving the element that must rotate into position i,j
in the next step. The function g
that does all of this and forms the new matrix after a single step is then called repeatedly by evaling a generated string containing the representation of a nested function call.
When I made this originally, I accidentally caused the matrix to rotate clockwise instead of counterclockwise. Rather than doing a proper fix, I added two strategically placed copies of [::-1]
to reverse the matrix before and after the rotation. These could probably be golfed off to ~280 276 bytes, but I'm too lazy to do that.
Also, this is a quick untested port from a slightly longer Python 2 program, so forgive me if it doesn't work quite right. Here's the Python 2 code, anyways:
_=eval
a=raw_input().split()
b,a=int(a[0]),_("".join(a[1:]))[::-1]
c=len(a)
d=len(a[0])
e=xrange
f="int((i>=j and i+j<c-1)|2*(i>=c/2and i+d>j+c)|3*(i<c/2and i+j<d))"
l=[-1,1,0,0],[0,0,1,-1]
g=lambda x:[[x[i+l[0][_(f)]][j+l[1][_(f)]]for j in e(d)]for i in e(c)]
print _("g("*b+"a"+")"*b)[::-1]
EDIT: Golfed off 4 bytes by replacing or
with |
twice. and
can't be helped, unfortunately.