Beware of the matrix tornado!

27

1

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]]

ibrahim mahrir

Posted 2018-07-31T23:27:14.143

Reputation: 551

I think that you want to clarify that the rotations are 90° rotations. – Erik the Outgolfer – 2018-07-31T23:33:56.307

Also, have you taken this challenge from somewhere else? If so, you must provide attribution. – Erik the Outgolfer – 2018-07-31T23:37:21.097

1@EriktheOutgolfer 1) I've clarified that. 2) This challenge is mine. – ibrahim mahrir – 2018-07-31T23:39:09.973

90 degrees clockwise or anti-clockwise? – Giuseppe – 2018-08-01T00:04:01.893

4@Giuseppe Depends on which hemisphere you're in ;) – Jo King – 2018-08-01T00:05:37.387

12

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! :)

– FryAmTheEggman – 2018-08-01T01:43:03.777

@FryAmTheEggman Thank you for your input! I've removed that restriction. – ibrahim mahrir – 2018-08-01T10:37:31.243

Answers

5

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!

Aneesh Durg

Posted 2018-07-31T23:27:14.143

Reputation: 239

8Classic 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 – ETHproductions – 2018-08-01T00:03:36.327

1@ETHproductions To be fair, part of that is syntax inherited from numpy – Jo King – 2018-08-01T01:21:18.373

1numpy.rot90(a,1,(1,0)) is shorter by 3 bytes and should also work. – Graipher – 2018-08-01T08:27:24.600

1

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).

– Kevin Cruijssen – 2018-08-01T09:23:16.373

5

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.

Neil

Posted 2018-07-31T23:27:14.143

Reputation: 95 035

5

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.

Lynn

Posted 2018-07-31T23:27:14.143

Reputation: 55 648

1You can probably just put µG in the footer and claim that your submission is 25. – Mr. Xcoder – 2018-08-01T08:01:15.993

5

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
}

Jo King

Posted 2018-07-31T23:27:14.143

Reputation: 38 234

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.) – nwellnhof – 2018-08-01T11:04:50.087

But @a[1..*-2;1..@a-2].=$! works. – nwellnhof – 2018-08-01T11:21:48.550

5

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.

Sanchises

Posted 2018-07-31T23:27:14.143

Reputation: 8 530

+1 for southern hemisphere – ceilingcat – 2018-08-04T01:28:04.833

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.

– Stewie Griffin – 2019-09-20T06:39:31.700

@StewieGriffin I shall see what I can do :) – Sanchises – 2019-09-20T11:31:19.663

@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. – Sanchises – 2019-09-20T14:04:10.743

4

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!

digEmAll

Posted 2018-07-31T23:27:14.143

Reputation: 4 599

84 bytes rotating in the opposite direction as the examples – Giuseppe – 2018-08-01T13:34:19.140

Is it allowed? The image shows a clockwise arrow and the description below it states clockwise rotation... – digEmAll – 2018-08-01T14:24:16.707

I must have read the question ten times and never noticed it states clockwise (hence my comment). Alas. – Giuseppe – 2018-08-01T14:25:43.490

Eheh, tell me about it... I'm the king of misreading posts :D – digEmAll – 2018-08-01T14:49:55.497

I think you can just use seq(n/2) rather than seq(l=n%/%2) : seq(3.5) returns 1 2 3 for example – JDL – 2018-08-02T07:31:52.973

1Unfortunately the 1x1 matrix won't work (because seq(0.5) returns 1 instead of empty vector ) – digEmAll – 2018-08-02T07:54:05.407

4

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.

Sanchises

Posted 2018-07-31T23:27:14.143

Reputation: 8 530

3

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!

TFeld

Posted 2018-07-31T23:27:14.143

Reputation: 19 246

3

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

Arnauld

Posted 2018-07-31T23:27:14.143

Reputation: 111 334

3

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

ngn

Posted 2018-07-31T23:27:14.143

Reputation: 11 449

2I'll be glad to see the explanation of your code – Galen Ivanov – 2018-08-01T09:19:02.423

1@GalenIvanov Sure. I don't think I can golf this any further, so I might as well try to explain it. – ngn – 2018-08-01T09:21:00.593

Thanks! Your solutions make me want to start learning k (or even ngn/k :) ) – Galen Ivanov – 2018-08-01T10:32:48.503

@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.

– ngn – 2018-08-01T13:01:58.210

Yes, I think I'm going to try it. – Galen Ivanov – 2018-08-02T06:17:43.217

3

Jelly, 24 bytes

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

Try it online!

I think this could be far shorter.

Lynn

Erik the Outgolfer

Posted 2018-07-31T23:27:14.143

Reputation: 38 134

I wondered about a solution like this! That ḷ"" looks magical to me ^^ care to add an explanation? – Lynn – 2018-08-02T09:55:07.993

@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). – Erik the Outgolfer – 2018-08-02T10:06:00.040

2

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.

Kevin Cruijssen

Posted 2018-07-31T23:27:14.143

Reputation: 67 575

2

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.

Lynn

Posted 2018-07-31T23:27:14.143

Reputation: 55 648

1

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

AlexJ136

Posted 2018-07-31T23:27:14.143

Reputation: 251

You may want to check out our tips for golfing in Haskell, e.g. Using guards instead of conditionals will save some bytes.

– Laikoni – 2018-08-02T05:48:25.340

1

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

Jacob Watson

Posted 2018-07-31T23:27:14.143

Reputation: 131

1

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!

vazt

Posted 2018-07-31T23:27:14.143

Reputation: 311