views:

884

answers:

4

Question: given an integer number n, print the numbers from 1 up to n2 like this:

n = 4

result is:

01 02 03 04
12 13 14 05
11 16 15 06
10 09 08 07

How do you solve it (apart from the solution provided in the link below)?

http://www.programmersheaven.com/mb/CandCPP/81986/81986/problem-in-making-ap-c++-program/?S=B20000

I'm looking in another direction. So far, I'm trying to figure out if I could obtain the ordered list of positions I have to fill in.

Here's what Im looking into: is there a way to obtain the "fdisp" so as to solve the problem that way, instead of "walk" in the matrix?

matrix = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
n = len(matrix)

# final disposition wrote by hand: how to get it for arbitrary n?
fdisp = [(0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (3,3), (3,2),
   (3,1), (3,0), (2,0), (1,0), (1,1), (1,2), (2,2), (2,1)]

for val,i in enumerate(fdisp):
 matrix[i[0]][i[1]] = val + 1

def show_matrix(matrix, n):
 for i,l in enumerate(matrix):
  for j in range(n):
   print "%d\t" % matrix[i][j],
  print

show_matrix(matrix, n)
+1  A: 

Though your example is in python and this is in Java, I think you should be able to follow the logic:

public class SquareTest {

public static void main(String[] args) {
 SquareTest squareTest = new SquareTest(4);
 System.out.println(squareTest);
}

private int squareSize;
private int[][] numberSquare;
private int currentX;
private int currentY;
private Direction currentDirection;

private enum Direction {
 LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM, BOTTOM_TO_TOP;
};

public SquareTest(int squareSize) {
 this.squareSize = squareSize;
 numberSquare = new int[squareSize][squareSize];
 currentY = 0;
 currentX = 0;
 currentDirection = Direction.LEFT_TO_RIGHT;
 constructSquare();
}

private void constructSquare() {
 for (int i = 0; i < squareSize * squareSize; i = i + 1) {
  numberSquare[currentY][currentX] = i + 1;
  if (Direction.LEFT_TO_RIGHT.equals(currentDirection)) {
   travelLeftToRight();
  } else if (Direction.RIGHT_TO_LEFT.equals(currentDirection)) {
   travelRightToLeft();
  } else if (Direction.TOP_TO_BOTTOM.equals(currentDirection)) {
   travelTopToBottom();
  } else {
   travelBottomToTop();
  }
 }
}

private void travelLeftToRight() {
 if (currentX + 1 == squareSize || numberSquare[currentY][currentX + 1] != 0) {
  currentY = currentY + 1;
  currentDirection = Direction.TOP_TO_BOTTOM;
 } else {
  currentX = currentX + 1;
 }
}

private void travelRightToLeft() {
 if (currentX - 1 < 0 || numberSquare[currentY][currentX - 1] != 0) {
  currentY = currentY - 1;
  currentDirection = Direction.BOTTOM_TO_TOP;
 } else {
  currentX = currentX - 1;
 }
}

private void travelTopToBottom() {
 if (currentY + 1 == squareSize || numberSquare[currentY + 1][currentX] != 0) {
  currentX = currentX - 1;
  currentDirection = Direction.RIGHT_TO_LEFT;
 } else {
  currentY = currentY + 1;
 }
}

private void travelBottomToTop() {
 if (currentY - 1 < 0 || numberSquare[currentY - 1][currentX] != 0) {
  currentX = currentX + 1;
  currentDirection = Direction.LEFT_TO_RIGHT;
 } else {
  currentY = currentY - 1;
 }
}

@Override
public String toString() {
 StringBuilder builder = new StringBuilder();
 for (int i = 0; i < squareSize; i = i + 1) {
  for (int j = 0; j < squareSize; j = j + 1) {
   builder.append(numberSquare[i][j]);
   builder.append(" ");
  }
  builder.append("\n");
 }

 return builder.toString();
}
}
Peter Nix
Thank you, but I was looking for another way to solve it
gmoh
+1  A: 

Another way to do it, this time in C#:

int number = 9;
var position = new { x = -1, y = 0 };
var directions = new [] { 
    new { x = 1, y = 0 },
    new { x = 0, y = 1 },
    new { x = -1, y = 0 },
    new { x = 0, y = -1 }
};

var sequence = (
    from n in Enumerable.Range(1, number)
    from o in Enumerable.Repeat(n, n != number ? 2 : 1)
    select o
).Reverse().ToList();

var result = new int[number,number];

for (int i = 0, current = 1; i < sequence.Count; i++)
{
    var direction = directions[i % directions.Length];  

    for (int j = 0; j < sequence[i]; j++, current++)
    {
     position = new {
      x = position.x + direction.x,
      y = position.y + direction.y
     };

     result[position.y, position.x] = current;
    }
}
Michael Morton
Thank you, really interesting solution
gmoh
+1  A: 

I found a way. Now I've to improve it a bit, especially I've to find a cleaner way to build "fdisp". n = 5

dim = n
pos = (0, -1)
fdisp = []
squares = n % 2 == 0 and n / 2 or n / 2 + 1

for _ in range(squares):
 pos = (pos[0], pos[1] + 1)
 fdisp.append(pos)

 fdisp += [(pos[0],pos[1]+i) for i in range(1, dim)]
 pos = fdisp[-1]
 fdisp += [(pos[0]+i,pos[1]) for i in range(1, dim)]
 pos = fdisp[-1]
 fdisp += [(pos[0],pos[1]-i) for i in range(1, dim)]
 pos = fdisp[-1]
 fdisp += [(pos[0]-i,pos[1]) for i in range(1, dim - 1)]
 pos = fdisp[-1]
 dim = dim - 2

matrix = [[0] * n for i in range(n)]

for val,i in enumerate(fdisp):
 matrix[i[0]][i[1]] = val + 1

def show_matrix(matrix, n):
 for i,l in enumerate(matrix):
  for j in range(n):
   print "%d\t" % matrix[i][j],
  print

show_matrix(matrix, n)
gmoh
+4  A: 

Here's a different approach. It relies on spotting that the movements you make cycle between: right, down, left, up, right, .... Further, the number of times you move goes: 3 right, 3 down, 3 left, 2 up, 2 right, 1 down, 1 left. So without further ado, I will code this up in Python.

First, I will use some itertools and some numpy:

from itertools import chain, cycle, imap, izip, repeat
from numpy import array

The directions cycle between: right, down, left, up, right, ...:

directions = cycle(array(v) for v in ((0,1),(1,0),(0,-1),(-1,0)))

(I'm using numpy's arrays here so I can easily add directions together. Tuples don't add nicely.)

Next, the number of times I move counts down from n-1 to 1, repeating each number twice, and the first number three times:

countdown = chain((n-1,), *imap(repeat, range(n-1,0,-1), repeat(2)))

So now my sequence of directions can be created by repeating each successive direction by the paired number in countdown:

dirseq = chain(*imap(repeat, directions, countdown))

To get my sequence of indices, I can just sum this sequence, but (AFAIK) Python does not provide such a method, so let's quickly throw one together:

def sumseq(seq, start=0):
  v = start
  yield v
  for s in seq:
    v += s
    yield v

Now to generate the original array, I can do the following:

a = array(((0,)*n,)*n) # n-by-n array of zeroes
for i, v in enumerate(sumseq(dirseq, array((0,0)))):
  a[v[0], v[1]] = i+1
print a

Which, for n = 4, gives:

[[ 1  2  3  4]
 [12 13 14  5]
 [11 16 15  6]
 [10  9  8  7]]

and, for n = 5, gives:

[[ 1  2  3  4  5]
 [16 17 18 19  6]
 [15 24 25 20  7]
 [14 23 22 21  8]
 [13 12 11 10  9]]

This approach can be generalised to rectangular grids; I leave this as an exercise for the reader ;)

chrispy
This is very clever, I've to study it carefully.
gmoh