8

i wanted ask if there some algorithm ready, that allowed me to do this: i have a matrix m (col) x n (row) with m x n elements. I want give position to this element starting from center and rotating as a spiral, for example, for a matrix 3x3 i have 9 elements so defined:

 5  6  7
 4  9  8
 3  2  1 

or for una matrix 4 x 3 i have 12 elements, do defined:

  8   9  10  1
  7  12  11  2
  6   5   4  3

or again, a matrix 5x2 i have 10 elements so defined:

    3  4
    7  8
   10  9
    6  5 
    2  1

etc. I have solved basically defining a array of integer of m x n elements and loading manually the value, but in generel to me like that matrix maked from algorithm automatically. Thanks to who can help me to find something so, thanks very much.

UPDATE

This code, do exactely about i want have, but not is in delphi; just only i need that start from 1 and not from 0. Important for me is that it is valid for any matrics m x n. Who help me to translate it in delphi?

(defun spiral (rows columns)  
  (do ((N (* rows columns))       
    (spiral (make-array (list rows columns) :initial-element nil))      
    (dx 1) (dy 0) (x 0) (y 0)    
   (i 0 (1+ i)))     
 ((= i N) spiral)   
 (setf (aref spiral y x) i)
    (let ((nx (+ x dx)) (ny (+ y dy)))  
    (cond       
       ((and (< -1 nx columns)       
       (< -1 ny rows)           
       (null (aref spiral ny nx)))    
    (setf x nx 
          y ny))  
     (t (psetf dx (- dy)                 
       dy dx)       
    (setf x (+ x dx)             
     y (+ y dy))))))) 


> (pprint (spiral 6 6))

#2A ((0  1  2  3  4  5)
    (19 20 21 22 23  6)
    (18 31 32 33 24  7)
    (17 30 35 34 25  8)
    (16 29 28 27 26  9)
    (15 14 13 12 11 10))


> (pprint (spiral 5 3))

#2A  ((0  1 2)
     (11 12 3)
     (10 13 4)
      (9 14 5)
      (8 7 6))

Thanks again very much.

Marcello Impastato
  • 2,263
  • 5
  • 30
  • 52
  • 4
    Your algorithm is not yet well-defined in my view. I expected the 5x2 matrix to be ((6 7) (5 8) (4 9) (3 10) (2 1)). – David Heffernan Jan 06 '12 at 12:32
  • 2
    I'm with David. And why does 1 start in different places? Should be the same corner every time. – Chris Thornton Jan 06 '12 at 12:33
  • The only clear thing is that it seems to proceed clockwise! Can you tell a concrete usage of the algorithm? – menjaraz Jan 06 '12 at 12:48
  • 4
    I don't understand your algorithm. it is not consistent. the classic spiral algorithm for example of 3x3 should be: `(1 2 3)(8 9 4)(7 6 5)`. [take a look here](http://rosettacode.org/wiki/Spiral_matrix). – kobik Jan 06 '12 at 12:59
  • I dont believe that is a real algorithm and have algorithm's properties. kobik's link implicitly confirms that, lacking Pascal implementation. Anyway, this can be easily solved using polar coordinates. – OnTheFly Jan 06 '12 at 13:48
  • @user539484 I'd be interested to hear how polar coords could apply here. I've only come across polar coords in complex plane or 2-space, i.e. spaces over continuous fields. Do you know any examples of polar coords in such a setting? – David Heffernan Jan 06 '12 at 14:34
  • 2
    Hello, much probably i haven't told correctly "spiral". It was just for give idea that number in matrix are placed designing a spiral for example for 6 number it start from 6 to 1: 6, 5, 4, 3, 2, 1. The element 6 is placed to center of matrix, and index of matrix rotate from left to right as a spiral. Having so result as in example. Forgive me again for my bad english. The matrix with this elements so setted is applied to random numbers. – Marcello Impastato Jan 06 '12 at 15:27
  • kobik: your example is correct, The difference is that in my case in a matrix 3x3, i start from center of it and not from first posiztion in tot-left. – Marcello Impastato Jan 06 '12 at 15:34
  • 3
    For a start, you can check here http://www.technicalinterviewquestions.net/2009/03/print-2d-array-matrix-spiral-order.html – A Lombardo Jan 06 '12 at 15:39
  • 1
    Kobik, i have looked better your site, it solve my problem but solution is for square matrix, for general matrix m x n i have found in c, can help me to tranlate it in delphi? Thanks very much again – Marcello Impastato Jan 06 '12 at 18:38
  • 1
    @Marcello, you should post the c code as an edit to your question and ask to convert to Delphi. your Q is not so clear anyway. take a look at [this](http://stackoverflow.com/questions/398299/looping-in-a-spiral). you just have to love Python! :) – kobik Jan 06 '12 at 19:05
  • Out of curiosity, is there a Delphi compiler/runner somewhere online? ideone.com doesn't have Delphi yet... – Joanis Jan 06 '12 at 22:13
  • @ChrisThornton: 1 ends. The algorithm must start from the center, go right if odd width and then up. The 1 will just be where the spiral ends. – Joanis Jan 06 '12 at 22:17
  • @DavidHeffernan & ChrisThornton: It starts numbering (descending order) from the center. That's why a spiral of width 2 looks weird. – Joanis Jan 06 '12 at 22:20
  • @Marcello: I'd be curious to see what a spiral of height 2 is supposed to look like. You haven't shown any example with even height. – Joanis Jan 06 '12 at 22:21
  • @M.Joanis ideone.com has free pascal – David Heffernan Jan 06 '12 at 22:24
  • @DavidHeffernan: Oh I did some Delphi a long time ago. I know it's close parent with Pascal, but didn't thought you could actually compile Delphi with a Pascal compiler. I might try it. Thanks. – Joanis Jan 06 '12 at 22:39
  • @M.Joanis FPC is an excellent open source compiler that uses a variant of object pascal that is very similar and highly compatible with delphi – David Heffernan Jan 06 '12 at 22:40
  • @DavidHeffernan: Great, didn't know which one to play with (there are two as you probably know). Thanks for the info! – Joanis Jan 06 '12 at 22:55
  • Another version of a spiral matrix is discussed here http://stackoverflow.com/questions/20926038/how-to-print-numbers-in-a-spiral-order/ – user1232296 Jan 06 '14 at 14:48

4 Answers4

4

Based on the classic spiral algorithm. supporting non-square matrix:

program SpiralMatrix;
{$APPTYPE CONSOLE}
uses
  SysUtils;

type
  TMatrix = array of array of Integer;

procedure PrintMatrix(const a: TMatrix);
var
  i, j: Integer;
begin
  for i := 0 to Length(a) - 1 do
  begin
    for j := 0 to Length(a[0]) - 1 do
      Write(Format('%3d', [a[i, j]]));
    Writeln;
  end;
end;

var
  spiral: TMatrix;
  i, m, n: Integer;
  row, col, dx, dy,
  dirChanges, visits, temp: Integer;
begin
  m := 3; // columns
  n := 3; // rows
  SetLength(spiral, n, m);
  row := 0;
  col := 0;
  dx := 1;
  dy := 0;
  dirChanges := 0;
  visits := m;
  for i := 0 to n * m - 1 do
  begin
    spiral[row, col] := i + 1;
    Dec(visits);
    if visits = 0 then
    begin
      visits := m * (dirChanges mod 2) + n * ((dirChanges + 1) mod 2) - (dirChanges div 2) - 1;
      temp := dx;
      dx := -dy;
      dy := temp;
      Inc(dirChanges);
    end;
    Inc(col, dx);
    Inc(row, dy);
  end;
  PrintMatrix(spiral);
  Readln;
end.

3 x 3:

1  2  3
8  9  4
7  6  5

4 x 3:

 1  2  3  4
10 11 12  5
 9  8  7  6

2 x 5:

 1  2
10  3
 9  4
 8  5
 7  6
kobik
  • 21,001
  • 4
  • 61
  • 121
1

There you go!!! After 30some syntax errors...

On ideone.com, I ran it with some tests and it seems to work fine. I think you can see the output there still and run it yourself...

I put some comments in the code. Enough to understand most of it. The main navigation system is a little bit harder to explain. Briefly, doing a spiral is going in first direction 1 time, second 1 time, third 2 times, fourth 2 times, fifth 3 times, 3, 4, 4, 5, 5, and so on. I use what I called a seed and step to get this behavior.

program test;

var
    w, h, m, n, v, d : integer; // Matrix size, then position, then value and direction.
    spiral : array of array of integer; // Matrix/spiral itself.
    seed, step : integer; // Used to travel the spiral.

begin
    readln(h);
    readln(w);
    setlength(spiral, h, w);
    v := w * h; // Value to put in spiral.
    m := trunc((h - 1) / 2);  // Finding center.
    n := trunc((w - 1) / 2);
    d := 0; // First direction is right.

    seed := 2;
    step := 1;

    // Travel the spiral.
    repeat
        // If in the sub-spiral, store value.
        if ((m >= 0) and (n >= 0) and (m < h) and (n < w)) then
        begin
            spiral[m, n] := v;
            v := v - 1;
        end;

        // Move!
        case d of
            0: n := n + 1;
            1: m := m - 1;
            2: n := n - 1;
            3: m := m + 1;
        end;

        // Plan trajectory.
        step := step - 1;
        if step = 0 then
        begin
            d := (d + 1) mod 4;
            seed := seed + 1;
            step := trunc(seed / 2);
        end;
    until v = 0;

    // Print the spiral.
    for m := 0 to (h - 1) do
    begin
        for n := 0 to (w - 1) do
        begin
            write(spiral[m, n], ' ');
        end;
        writeln();
    end;

end.

If you really need that to print text spirals I'll let you align the numbers. Just pad them with spaces.

EDIT:

Was forgetting... In order to make it work on ideone, I put the parameters on 2 lines as input. m, then n.

For example:

5
2

yields

3 4 
7 8 
10 9 
6 5 
2 1 
Joanis
  • 1,669
  • 3
  • 20
  • 32
  • 1
    Thanks very much Joanis. This solution is about i searched, It work very very well. Thanks again; thanks to all other that has help me too. – Marcello Impastato Jan 07 '12 at 02:01
  • it produced some strange spirals when I tested with 2x2 and 4x3 – kobik Jan 07 '12 at 13:48
  • @kobik: Yeah, not all cases are giving what we can really call spirals. The center is defined by `floor((height-1)/2)` and `floor((width-1)/2)` **and** the sequence of directions starts with "right", then "up"... That's what explains the weird not-spirals. The center of the 2x2 is in (0, 0)=4, then it moves to (0, 1)=3, until there it makes "sense", but then we have to go up and there's nothing up, so we go left, come back, and continue the spiral on the second row: 2, and 1. That's why you get weird results like (4 3) (2 1) for a 2x2. – Joanis Jan 07 '12 at 15:56
-1

Even though the question is already answered, this is an alternative solution (arguably simpler). The solution is in python (using numpy for bidimendional arrays), but can be easily ported.

The basic idea is to use the fact that the number of steps is known (m*n) as end condition, and to properly compute the next element of the loop at each iteration:

import numpy as np

def spiral(m, n):
    """Return a spiral numpy array of int with shape (m, n)."""
    a = np.empty((m, n), int)
    i, i0, i1 = 0, 0, m - 1
    j, j0, j1 = 0, 0, n - 1
    for k in range(m * n):
        a[i, j] = k
        if   i == i0 and     j0 <= j <  j1: j += 1
        elif j == j1 and     i0 <= i <  i1: i += 1
        elif i == i1 and     j0 <  j <= j1: j -= 1
        elif j == j0 and 1 + i0 <  i <= i1: i -= 1
        else:
            i0 += 1
            i1 -= 1
            j0 += 1
            j1 -= 1
            i, j = i0, j0
    return a

And here some outputs:

>>> spiral(3,3)
array([[0, 1, 2],
       [7, 8, 3],
       [6, 5, 4]])
>>> spiral(4,4)
array([[ 0,  1,  2,  3],
       [11, 12, 13,  4],
       [10, 15, 14,  5],
       [ 9,  8,  7,  6]])
>>> spiral(5,4)
array([[ 0,  1,  2,  3],
       [13, 14, 15,  4],
       [12, 19, 16,  5],
       [11, 18, 17,  6],
       [10,  9,  8,  7]])
>>> spiral(2,5)
array([[0, 1, 2, 3, 4],
       [9, 8, 7, 6, 5]])
joanpau
  • 568
  • 5
  • 14
-1

Here's the commented JavaScript implementation for what you're trying to accomplish.

// return an array representing a matrix of size MxN COLxROW
function spiralMatrix(M, N) {
var result = new Array(M * N);
var counter = M * N;
// start position
var curCol = Math.floor((M - 1) / 2);
var curRow = Math.floor(N / 2);
// set the center
result[(curRow * M) + curCol] = counter--;
// your possible moves RIGHT, UP, LEFT, DOWN * y axis is flipped
var allMoves = [[1,0], [0,-1], [-1,0], [0,1]];
var curMove = 0;
var moves = 1; // how many times to make current Move, 1,1,2,2,3,3,4,4 etc
// spiral
while(true) {
 for(var i = 0; i < moves; i++) {
  // move in a spiral outward counter clock-wise direction
  curCol += allMoves[curMove][0];
  curRow += allMoves[curMove][1];
  // naively skips locations that are outside of the matrix bounds
  if(curCol >= 0 && curCol < M && curRow >= 0 && curRow < N) {
   // set the value and decrement the counter
   result[(curRow * M) + curCol] = counter--;
   // if we reached the end return the result
   if(counter == 0) return result;
  }
 }
 // increment the number of times to move if necessary UP->LEFT and DOWN->RIGHT
 if(curMove == 1 || curMove == 3) moves++;
 // go to the next move in a circular array fashion
 curMove = (curMove + 1) % allMoves.length;
}
}

The code isn't the most efficient, because it walks the spiral naively without first checking if the location it's walking on is valid. It only checks the validity of the current location right before it tries to set the value on it.

Louis Ricci
  • 20,804
  • 5
  • 48
  • 62