32
\$\begingroup\$

The matrix tornado is just like any other tornado: it consists of things rotating around a center. In this case, elements of the matrix instead of air.

Here is an example of a matrix tornado:

Matrix tornado in action

First we start by sectioning the matrix into square rings, each section consists of elements that are farther away from the border by the same distance. These sections will be rotated clockwise around the center. In real tornadoes, the severity increases towards the center, and so does the rotation step in a matrix tornado: the outermost section (the red one) is rotated by 1 step, the next (the yellow) one is rotated by 2, and so on. A rotation step is a 90° rotation around the center.

Task:

Your task, should you accept it, is to write a function or program that takes as input a square matrix, apply the tornado effect to it and then output the resulting matrix.

Input:

The input should be a square matrix of order n where n >= 1. No assumption is to be made about the elements of the matrix, they could be anything.

Output:

A square matrix of the same order which would be the result of applying the tronado effect to the input matrix.

Examples:

A matrix of order n = 1:

[['Hello']]               ===>    [['Hello']]

A matrix of order n = 2:

[[1 , 2],                 ===>    [[5 , 1],
 [5 , 0]]                          [0 , 2]]

A matrix of order n = 5:

[[A , B , C , D , E],             [[+ , 6 , 1 , F , A],
 [F , G , H , I , J],              [- , 9 , 8 , 7 , B],
 [1 , 2 , 3 , 4 , 5],     ===>     [/ , 4 , 3 , 2 , C],
 [6 , 7 , 8 , 9 , 0],              [* , I , H , G , D],
 [+ , - , / , * , %]]              [% , 0 , 5 , J , E]]
\$\endgroup\$
7
  • \$\begingroup\$ I think that you want to clarify that the rotations are 90° rotations. \$\endgroup\$ Jul 31, 2018 at 23:33
  • \$\begingroup\$ Also, have you taken this challenge from somewhere else? If so, you must provide attribution. \$\endgroup\$ Jul 31, 2018 at 23:37
  • 1
    \$\begingroup\$ @EriktheOutgolfer 1) I've clarified that. 2) This challenge is mine. \$\endgroup\$ Jul 31, 2018 at 23:39
  • 4
    \$\begingroup\$ @Giuseppe Depends on which hemisphere you're in ;) \$\endgroup\$
    – Jo King
    Aug 1, 2018 at 0:05
  • 13
    \$\begingroup\$ First I'd like to say I think this is a good challenge: nice work! But I'd also like to plug this point because I think your choice to say that it could be any type of data leaves your challenge in an awkward spot. Similarly with your statement about the input being a list of lists, you have restricted the languages that can solve this problem without doing overhead work somewhat. I think the challenge is better if these requirements are relaxed. I hope you continue to post nice challenges like this! :) \$\endgroup\$ Aug 1, 2018 at 1:43

20 Answers 20

6
\$\begingroup\$

Python 3, 100 97 96 bytes

-3 bytes thanks to Graipher by changing rot90(a,axes=(1,0)) to rot90(a,1,(1,0))
-1 byte thanks to Kevin Cruijssen by removing a space

import numpy
def f(a):
 if len(a):a=numpy.rot90(a,1,(1,0));a[1:-1,1:-1]=f(a[1:-1,1:-1]);return a

Try it online!

\$\endgroup\$
5
  • 9
    \$\begingroup\$ Classic Python, just dropping a[1:-1,1:-1]=f(a[1:-1,1:-1]) like it's the most normal thing in the world to directly get and set the entire inside of a 2-dimensional array \$\endgroup\$ Aug 1, 2018 at 0:03
  • 2
    \$\begingroup\$ @ETHproductions To be fair, part of that is syntax inherited from numpy \$\endgroup\$
    – Jo King
    Aug 1, 2018 at 1:21
  • 3
    \$\begingroup\$ numpy.rot90(a,1,(1,0)) is shorter by 3 bytes and should also work. \$\endgroup\$
    – Graipher
    Aug 1, 2018 at 8:27
  • 2
    \$\begingroup\$ What's the point of the TIO-link without any test cases?.. :S Here it is with (dropped the space at if len(a):a=... for -1 byte). \$\endgroup\$ Aug 1, 2018 at 9:23
  • \$\begingroup\$ 71 bytes by using a[::-1].T instead of numpy.rot90(a): (so you don't need to import numpy) Try it online! \$\endgroup\$
    – Stef
    Feb 13 at 12:09
5
\$\begingroup\$

Charcoal, 44 bytes

≔EθSθWθ«≔Eθ⮌⭆觧θνλθθM¹⁻¹Lθ≔E✂θ¹±¹¦¹✂κ¹±¹¦¹θ

Try it online! Link is to verbose version of code. Only works on character squares because Charcoal's default I/O doesn't do normal arrays justice. Explanation:

≔EθSθ

Read the character square.

Wθ«

Loop until it's empty.

≔Eθ⮌⭆觧θνλθ

Rotate it.

θM¹⁻¹Lθ

Print it, but then move the cursor to one square diagonally in from the original corner.

≔E✂θ¹±¹¦¹✂κ¹±¹¦¹θ

Trim the outside from the array.

\$\endgroup\$
5
\$\begingroup\$

Jelly, 27 bytes

J«þ`UṚ«Ɗ‘ịZU$LСŒĖḢŒHEƊƇṁµG

Try it online!

I think this could be far shorter.

           Input: n×n matrix A.
J          Get [1..n].
 «þ`       Table of min(x, y).
    UṚ«Ɗ   min with its 180° rotation.

Now we have a matrix like: 1 1 1 1 1
                           1 2 2 2 1
                           1 2 3 2 1
                           1 2 2 2 1
                           1 1 1 1 1

‘ị          Increment all, and use as indices into...
     LС    List of [A, f(A), f(f(A)), …, f^n(A)]
  ZU$       where f = rotate 90°

Now we have a 4D array (a 2D array of 2D arrays).
We wish to extract the [i,j]th element from the [i,j]th array.

ŒĖ     Multidimensional enumerate

This gives us: [[[1,1,1,1],X],
                [[1,1,1,2],Y],
                ...,
                [[n,n,n,n],Z]]

ḢŒHEƊƇ     Keep elements whose Ḣead (the index) split into equal halves (ŒH)
           has the halves Equal to one another. i.e. indices of form [i,j,i,j]
           (Also, the head is POPPED from each pair, so now only [X] [Y] etc remain.)

ṁµG        Shape this like the input and format it in a grid.
\$\endgroup\$
1
  • 1
    \$\begingroup\$ You can probably just put µG in the footer and claim that your submission is 25. \$\endgroup\$
    – Mr. Xcoder
    Aug 1, 2018 at 8:01
5
\$\begingroup\$

Perl 6, 78 73 72 bytes

Thanks to nwellnhof for -5 bytes!

$!={my@a;{(@a=[RZ] rotor @_: sqrt @_)[1..*-2;1..@a-2].=$!}if @_;@a[*;*]}

Try it online!

Recursive code block that takes a flattened 2D array and returns a similarly flattened array.

Explanation:

$!={      # Assign code block to pre-declared variable $!
    my@a; # Create local array variable a
   {
     (@a=[RZ]  # Transpose:
             rotor @_: sqrt @_;  # The input array converted to a square matrix
     )[1..*-2;1..@a-2].=$!  # And recursively call the function on the inside of the array
   }if @_;    # But only do all this if the input matrix is not empty
   @a[*;*]  # Return the flattened array
}
\$\endgroup\$
2
  • \$\begingroup\$ You can use @a[*;*] instead of map |*,@a to flatten the array. (It would be nice if there was a way to work with unflattened arrays and multi-dimension subscripts, but I can't think of one.) \$\endgroup\$
    – nwellnhof
    Aug 1, 2018 at 11:04
  • \$\begingroup\$ But @a[1..*-2;1..@a-2].=$! works. \$\endgroup\$
    – nwellnhof
    Aug 1, 2018 at 11:21
5
\$\begingroup\$

Octave, 86 81 bytes

f(f=@(g)@(M,v=length(M))rot90({@(){M(z,z)=g(g)(M(z=2:v-1,z)),M}{2},M}{1+~v}(),3))

Try it online!

I'm aware that recursive anonymous functions are not the shortest method to do things in Octave, but they're the most fun method by far. This is the shortest anonymous function I could come up with, but I'd love to be outgolfed.

Explanation

The recursive function is defined according to this answer by ceilingcat. q=f(f=@(g)@(M) ... g(g)(M) ... is the basic structure of such an anonymous function, with g(g)(M) the recursive call. Since this would recurse indefinitely, we wrap the recursive call in a conditional cell array: {@()g(g)(M),M}{condition}(). The anonymous function with empty argument list delays evaluation to after the condition has been selected (although later, we see that we can use that argument list to define z). So far it has just been basic bookkeeping.

Now for the actual work. We want the function to return rot90(P,-1) with P a matrix on which g(g) has been recursively called on the central part of M. We start by setting z=2:end-1 which we can hide in the indexing of M. This way, M(z,z) selects the central part of the matrix that needs to be tornadoed further by a recursive call. The ,3 part ensures that the rotations are clockwise. If you live on the southern hemisphere, you may remove this bit for -2 bytes.

We then do M(z,z)=g(g)M(z,z). However, the result value of this operation is only the modified central part rather than the entire P matrix. Hence, we do {M(z,z)=g(g)M(z,z),M}{2} which is basically stolen from this answer by Stewie Griffin.

Finally, the condition is just that the recursion stops when the input is empty.

\$\endgroup\$
4
  • \$\begingroup\$ +1 for southern hemisphere \$\endgroup\$
    – ceilingcat
    Aug 4, 2018 at 1:28
  • \$\begingroup\$ I haven't tried to wrap my head around recursion in anonymous functions yet, so I won't give it an attempt, but I'm curious to see if recursion is shorter than loops in this one. \$\endgroup\$ Sep 20, 2019 at 6:39
  • \$\begingroup\$ @StewieGriffin I shall see what I can do :) \$\endgroup\$
    – Sanchises
    Sep 20, 2019 at 11:31
  • \$\begingroup\$ @StewieGriffin By the way, please feel challenged to post a loop-based version to this challenge in Octave. I really wonder if you can beat the recursive approach. \$\endgroup\$
    – Sanchises
    Sep 20, 2019 at 14:04
4
\$\begingroup\$

Python 2, 98 bytes

def f(a):
 if a:a=zip(*a[::-1]);b=zip(*a[1:-1]);b[-2:0:-1]=f(b[-2:0:-1]);a[1:-1]=zip(*b)
 return a

Try it online!

\$\endgroup\$
4
\$\begingroup\$

R, 87 bytes

function(m,n=nrow(m)){for(i in seq(l=n%/%2))m[j,j]=t(apply(m[j<-i:(n-i+1),j],2,rev));m}

Try it online!

\$\endgroup\$
6
  • \$\begingroup\$ 84 bytes rotating in the opposite direction as the examples \$\endgroup\$
    – Giuseppe
    Aug 1, 2018 at 13:34
  • \$\begingroup\$ Is it allowed? The image shows a clockwise arrow and the description below it states clockwise rotation... \$\endgroup\$
    – digEmAll
    Aug 1, 2018 at 14:24
  • \$\begingroup\$ I must have read the question ten times and never noticed it states clockwise (hence my comment). Alas. \$\endgroup\$
    – Giuseppe
    Aug 1, 2018 at 14:25
  • \$\begingroup\$ Eheh, tell me about it... I'm the king of misreading posts :D \$\endgroup\$
    – digEmAll
    Aug 1, 2018 at 14:49
  • 1
    \$\begingroup\$ Unfortunately the 1x1 matrix won't work (because seq(0.5) returns 1 instead of empty vector ) \$\endgroup\$
    – digEmAll
    Aug 2, 2018 at 7:54
4
\$\begingroup\$

MATL, 25 24 23 22

t"tX@Jyq-ht3$)3X!7Mt&(

Try it online!

Indexing in MATL is never easy, but with some golfing it actually beats the current best Jelly answer...

t                       % Take input implicitly, duplicate.  
 "                      % Loop over the columns of the input*
   X@                   % Push iteration index, starting with 0. Indicates the start of the indexing range.
     Jyq-               % Push 1i-k+1 with k the iteration index. Indicates the end of the indexing range
         t              % Duplicate for 2-dimensional indexing.
  t       3$)           % Index into a copy of the matrix. In each loop, the indexing range gets smaller
             3X!        % Rotate by 270 degrees anti-clockwise
                7Mt&(   % Paste the result back into the original matrix. 

*For an n x n matrix, this program does n iterations, while you really only need n/2 rotations. However, the indexing in MATL(AB) is sufficiently flexible that indexing impossible ranges is just a no-op. This way, there's no need to waste bytes on getting the number of iterations just right.

\$\endgroup\$
3
\$\begingroup\$

K (ngn/k), 41 39 38 bytes

{s#(+,/'4(+|:)\x)@'4!1+i&|i:&/!s:2##x}

Try it online!

{ } function with argument x

#x length of x - the height of the matrix

2##x two copies - height and width (assumed to be the same)

s: assign to s for "shape"

!s all indices of a matrix with shape s, e.g. !5 5 is

(0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4
 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4)

This is a 2-row matrix (list of lists) and its columns correspond to the indices in a 5x5 matrix.

&/ minimum over the two rows:

0 0 0 0 0 0 1 1 1 1 0 1 2 2 2 0 1 2 3 3 0 1 2 3 4

i&|i: assign to i, reverse (|), and take minima (&) with i

0 0 0 0 0 0 1 1 1 0 0 1 2 1 0 0 1 1 1 0 0 0 0 0 0

These are the flattened ring numbers of a 5x5 matrix:

4!1+ add 1 and take remainders modulo 4

(+|:) is a function that rotates by reversing (| - we need the : to force it to be monadic) and then transposing (+ - since it's not the rightmost verb in the "train", we don't need a :)

4(+|:)\x apply it 4 times on x, preserving intermediate results

,/' flatten each

+ transpose

( )@' index each value on the left with each value on the right

s# reshape to s

\$\endgroup\$
5
  • 2
    \$\begingroup\$ I'll be glad to see the explanation of your code \$\endgroup\$ Aug 1, 2018 at 9:19
  • 1
    \$\begingroup\$ @GalenIvanov Sure. I don't think I can golf this any further, so I might as well try to explain it. \$\endgroup\$
    – ngn
    Aug 1, 2018 at 9:21
  • \$\begingroup\$ Thanks! Your solutions make me want to start learning k (or even ngn/k :) ) \$\endgroup\$ Aug 1, 2018 at 10:32
  • \$\begingroup\$ @GalenIvanov Being familiar with J (and APL?), you are half-way there already. K is smaller and simpler, so I would highly recommend learning it and, of course, I'm happy to chat about it in the Orchard anytime. ngn/k is only a subset of the real thing but I do aim to make it fast and practical. \$\endgroup\$
    – ngn
    Aug 1, 2018 at 13:01
  • \$\begingroup\$ Yes, I think I'm going to try it. \$\endgroup\$ Aug 2, 2018 at 6:17
3
\$\begingroup\$

JavaScript (ES6), 99 bytes

f=(a,k=m=~-a.length/2)=>~k?f(a.map((r,y)=>r.map(v=>y-m>k|m-y>k|--x*x>k*k?v:a[m+x][y],x=m+1)),k-1):a

Try it online!

How?

Given a square matrix of width \$W\$, we define:

$$m=\frac{W-1}{2}\\ t_{x,y}=\max(|y-m|,|x-m|)$$

Example output of \$t_{x,y}\$ for a 5x5 matrix (\$W=5\$, \$m=2\$):

$$\begin{pmatrix} 2 & 2 & 2 & 2 & 2 \\ 2 & 1 & 1 & 1 & 2 \\ 2 & 1 & 0 & 1 & 2 \\ 2 & 1 & 1 & 1 & 2 \\ 2 & 2 & 2 & 2 & 2 \end{pmatrix}$$

We start with \$k=m\$ and perform a 90° clockwise rotation of all cells \$(x,y)\$ satisfying:

$$t_{x,y}\le k$$

while the other ones are left unchanged.

This is equivalent to say that a cell is not rotated if we have:

$$(y-m>k) \text{ OR } (m-y>k) \text{ OR } (X^2>k^2)\text{ with }X=m-x$$

which is the test used in the code:

a.map((r, y) =>
  r.map(v =>
    y - m > k | m - y > k | --x * x > k * k ?
      v
    :
      a[m + x][y],
    x = m + 1
  )
)

Then we decrement \$k\$ and start again, until \$k=-1\$ or \$k=-3/2\$ (depending on the parity of \$W\$). Either way, it triggers our halting condition:

~k === 0
\$\endgroup\$
3
\$\begingroup\$

Jelly, 24 bytes

ṙ⁹ṙ€
ḊṖ$⁺€ßḷ""ç1$ç-ZUµḊ¡

Try it online!

I think this could be far shorter.

Lynn

\$\endgroup\$
2
  • \$\begingroup\$ I wondered about a solution like this! That ḷ"" looks magical to me ^^ care to add an explanation? \$\endgroup\$
    – Lynn
    Aug 2, 2018 at 9:55
  • \$\begingroup\$ @Lynn The last thing I expected was to hear that ḷ"" is magical. It's just ḷ" with an extra "... oh, there's a slight possibility that ḷ" is also something I've "invented" that hasn't been used that much since it can oftentimes be replaced with a single atom (not in this case, as the input can contain 0 too). \$\endgroup\$ Aug 2, 2018 at 10:06
3
\$\begingroup\$

Haskell, 108 bytes

e=[]:e
r=foldl(flip$zipWith(:))e
g!(h:t)=h:g(init t)++[last t]
f[x,y]=r[x,y]
f[x]=[x]
f x=r$(r.r.r.(f!).r)!x

Try it online!

I used Laikoni's transpose and modified it a little, to rotate an array 90°:

  e=[]:e;foldr(zipWith(:))e.reverse
≡ e=[]:e;foldl(flip$zipWith(:))e

Explanation

r rotates an array by 90°.

(!) is a higher-level function: “apply to center”. g![1,2,3,4,5] is [1] ++ g[2,3,4] ++ [5].

f is the tornado function: the base cases are size 1 and 2 (somehow 0 doesn't work).

The last line is where the magic happens: we apply r.r.r.(f!).r on the middle rows of x and then rotate the result. Let's call those middle rows M. We want to recurse on the middle columns of M, and to get at those, we can rotate M and then use (f!). Then we use r.r.r to rotate M back into its original orientation.

\$\endgroup\$
3
\$\begingroup\$

J, 37 bytes

<./~@g`(|:@|.^:(1<@+>./@g=.#\.<.#\))}

Try it online!

Independently solved, but this approach is very similar to Lynn's.

It is also one of the most perfect problems for J's little used "composite item" } (in gerund form) that I have come across.

\$\endgroup\$
2
\$\begingroup\$

Java 10, 198 192 181 174 173 bytes

m->{int d=m.length,b=-1,i,j;var r=new Object[d][d];for(;b<d/2;){for(i=b++;++i<d-b;)for(j=b;j<d-b;)r[j][d+~i]=m[i][j++];for(i=d;i-->0;)m[i]=r[i].clone();}return r;}

-25 bytes thanks to @ceilingcat

Try it online.

Explanation:

m->{                      // Method with Object-matrix as both parameter & return-type
  int d=m.length,         //  Dimensions of the matrix
      b=-1,               //  Boundaries-integer, starting at -1
      i,j;                //  Index-integers
  var r=new Object[d][d]; //  Result-matrix of size `d` by `d`
  for(;b<d/2;){           //  Loop `b` in the range (-1,`d/2`]:
    for(i=b++;++i<d-b;)   //   Inner loop `i` in the range [`b`,`d-b`):
      for(j=b;j<d-b;)     //    Inner loop `j` in the range [`b`,`d-b`):
        r[j][d+~i]=       //     Set the `j,d-i-1`'th result-cell to:
          m[i][j++];      //      The `i,j`'th cell-value of the input-matrix
    for(i=d;i-->0;)       //   Inner loop `i` in the range (`d`,0]:
      m[i]                //    Change the `i`'th row of the input-matrix
        =r[i].clone();}   //     to a copy of the `i`'th row of the result-matrix
  return r;}              //  Return the result-matrix

b is basically used to indicate which ring we're at. And it will then rotate this ring (including everything inside it) once clockwise during every iteration.

The replacement of the input-matrix is done because Java is pass-by-reference, so simply setting r=m would mean both matrices are modified when copying from cells, causing incorrect results. We therefore instead have to copy the values in every cell one row at a time, without copying their reference (by using .clone()).

\$\endgroup\$
0
1
\$\begingroup\$

MATLAB, 93 bytes

function m=t(m),for i=0:nnz(m),m(1+i:end-i,1+i:end-i)=(rot90(m(1+i:end-i,1+i:end-i),3));end;end

I'm sure this can be golfed some more somehow.

Explanation

function m=t(m),                                                                          end % Function definition
                for i=0:nnz(m),                                                       end;    % Loop from 0 to n^2 (too large a number but matlab indexing doesn't care)
                                                            m(1+i:end-i,1+i:end-i)            % Take the whole matrix to start, and then smaller matrices on each iteration
                                                      rot90(                      ,3)         % Rotate 90deg clockwise (anti-clockwise 3 times)
                               m(1+i:end-i,1+i:end-i)=                                        % Replace the old section of the matrix with the rotated one
\$\endgroup\$
1
\$\begingroup\$

C (gcc), 128 118 115 bytes

-15 bytes from @ceilingcat

j,i;f(a,b,w,s)int*a,*b;{for(j=s;j<w-s;j++)for(i=s;i<w-s;)b[~i++-~j*w]=a[i*w+j];wmemcpy(a,b,w*w);++s<w&&f(a,b,w,s);}

Try it online!

\$\endgroup\$
0
1
\$\begingroup\$

Haskell, 274 bytes

w is the main function, which has the type [[a]] -> [[a]] that you would expect.

I'm sure a more experienced Haskell golfer could improve on this.

w m|t m==1=m|0<1=let m'=p m in(\a b->[h a]++x(\(o,i)->[h o]++i++[f o])(zip(tail a)b)++[f a])m'(w(g m'))
p m|t m==1=m|0<1=z(:)(f m)(z(\l->(l++).(:[]))(r(x h(i m)):(p(g m))++[r(x f(i m))])(h m))
t[]=1
t[[_]]=1
t _=0
h=head
f=last
x=map
i=tail.init
g=x i.i
z=zipWith
r=reverse
\$\endgroup\$
1
1
\$\begingroup\$

JavaScript (Node.js), 93 bytes

f=(a,k=0,L=a.length+~k)=>L?f(a.map((b,x)=>b.map((c,y)=>x<k|y<k|x>L|y>L?c:a[k+L-y][x])),k+1):a

Try it online!

f=(
  a,             // Input
  k=0,           // Range Left
  L=a.length+~k  // Range Right
)=>L?            // More than enough
  f(
    a.map((b,x)=>b.map((c,y)=>x<k|y<k|x>L|y>L?c:
      a[k+L-y][x]// k+L=a.length-1
  )),k+1)
:a
\$\endgroup\$
1
\$\begingroup\$

Haskell, 144 bytes

import Data.List
k=transpose
v=init.tail
t[[o]]=[[o]]
t m=k.reverse.(head m:).(++[last m]).
 k.((v.head$k m):).(++[v.last$k m]).
 k.t.map(v).v$m

Try it online!

v cuts out center (rows). map v cuts out center columns. t recurses into the tornado. k exposes columns as rows. (++[v.last$k m]) appends center of last column. ((v.head$k m):) prepends center of first column. k flips rows out again. (++[last m]) appends last row unprocessed. (head m:) prepends last row unprocessed. k.reverse rotates matrix.

\$\endgroup\$
0
\$\begingroup\$

Uiua SBCS, 25 bytes

∧(⍜(↘⊙↻|≡⇌⍉)ׯ2.⊟.)⇡⌈÷2⧻.

Try it!

Fun problem.

∧(⍜(↘⊙↻|≡⇌⍉)ׯ2.⊟.)⇡⌈÷2⧻.
                   ⇡⌈÷2⧻.  # create range for each ring
∧(                )        # fold over range and matrix, using matrix as accumulator
                ⊟.         # create pair indicating how much to rotate matrix on each axis
            ׯ2.           # and another pair indicating how much to drop from each axis
  ⍜(   |   )               # call the left part, then the right, then the inverse of left
        ≡⇌⍉                # rotate matrix clockwise
    ↘⊙↻                    # but only the interior
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.