27
\$\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\$
  • \$\begingroup\$ I think that you want to clarify that the rotations are 90° rotations. \$\endgroup\$ – Erik the Outgolfer Jul 31 '18 at 23:33
  • \$\begingroup\$ Also, have you taken this challenge from somewhere else? If so, you must provide attribution. \$\endgroup\$ – Erik the Outgolfer Jul 31 '18 at 23:37
  • 1
    \$\begingroup\$ @EriktheOutgolfer 1) I've clarified that. 2) This challenge is mine. \$\endgroup\$ – ibrahim mahrir Jul 31 '18 at 23:39
  • 4
    \$\begingroup\$ @Giuseppe Depends on which hemisphere you're in ;) \$\endgroup\$ – Jo King Aug 1 '18 at 0:05
  • 12
    \$\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\$ – FryAmTheEggman Aug 1 '18 at 1:43

16 Answers 16

5
\$\begingroup\$

Python 3, 100 bytes

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

Try it online!

\$\endgroup\$
  • 8
    \$\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\$ – ETHproductions Aug 1 '18 at 0:03
  • 1
    \$\begingroup\$ @ETHproductions To be fair, part of that is syntax inherited from numpy \$\endgroup\$ – Jo King Aug 1 '18 at 1:21
  • 1
    \$\begingroup\$ numpy.rot90(a,1,(1,0)) is shorter by 3 bytes and should also work. \$\endgroup\$ – Graipher Aug 1 '18 at 8:27
  • 1
    \$\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\$ – Kevin Cruijssen Aug 1 '18 at 9:23
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
    \$\begingroup\$ You can probably just put µG in the footer and claim that your submission is 25. \$\endgroup\$ – Mr. Xcoder Aug 1 '18 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\$
  • \$\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 '18 at 11:04
  • \$\begingroup\$ But @a[1..*-2;1..@a-2].=$! works. \$\endgroup\$ – nwellnhof Aug 1 '18 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\$
  • \$\begingroup\$ +1 for southern hemisphere \$\endgroup\$ – ceilingcat Aug 4 '18 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\$ – Stewie Griffin Sep 20 at 6:39
  • \$\begingroup\$ @StewieGriffin I shall see what I can do :) \$\endgroup\$ – Sanchises Sep 20 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 at 14:04
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\$
  • \$\begingroup\$ 84 bytes rotating in the opposite direction as the examples \$\endgroup\$ – Giuseppe Aug 1 '18 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 '18 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 '18 at 14:25
  • \$\begingroup\$ Eheh, tell me about it... I'm the king of misreading posts :D \$\endgroup\$ – digEmAll Aug 1 '18 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 '18 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\$

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\$
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\$
  • 2
    \$\begingroup\$ I'll be glad to see the explanation of your code \$\endgroup\$ – Galen Ivanov Aug 1 '18 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 '18 at 9:21
  • \$\begingroup\$ Thanks! Your solutions make me want to start learning k (or even ngn/k :) ) \$\endgroup\$ – Galen Ivanov Aug 1 '18 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 '18 at 13:01
  • \$\begingroup\$ Yes, I think I'm going to try it. \$\endgroup\$ – Galen Ivanov Aug 2 '18 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\$
  • \$\begingroup\$ I wondered about a solution like this! That ḷ"" looks magical to me ^^ care to add an explanation? \$\endgroup\$ – Lynn Aug 2 '18 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\$ – Erik the Outgolfer Aug 2 '18 at 10:06
2
\$\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\$
2
\$\begingroup\$

Java 10, 198 192 bytes

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

-6 bytes thanks to @ceilingcat.

Try it online.

Explanation:

m->{                         // Method with Object-matrix as both parameter and return-type
  int d=m.length,            //  Dimensions of the matrix
      b=0,                   //  Boundaries-integer, starting at 0
      i,j;                   //  Index-integers
  var r=new Object[d][d];    //  Result-matrix of size `d` by `d`
  for(;b<=d/2;b++){          //  Loop `b` in the range [0, `d/2`]
    for(i=b;i<d-b;i++)       //   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 result-cell at {`j`, `d-i-1`} to:
          m[i][j++];         //      The cell at {`i`, `j`} of the input-matrix
    for(m=new Object[d][d],  //   Empty the input-matrix
        i=d*d;i-->0;)        //   Inner loop `i` in the range (`d*d`, 0]
      m[i/d][i%d]            //     Copy the cell at {`i/d`, `i%d`} from the result-matrix
        =r[i/d][i%d];}       //      to the replaced input-matrix
  return r;}                 //  Return the result-matrix as result

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 have to create a new Object-matrix (new reference), and copy the values in every cell one-by-one instead.

\$\endgroup\$
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\$
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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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