3
\$\begingroup\$

This generates a square matrix with spiral inward increasing values. How can I reduce this code?

Input Number = 3

Output

1 2 3

8 9 4

7 6 5

import math
row=int(input("Input nomber := "))
lis=[[0 for i in range(0,row)]for j in range(0,row)]
y=row
a=1
c=0
z=0
x=1
k=1
f=row/2
f=math.ceil(f)
for b in range(0,f):
 if b==k:
 row=row-1
 x=x+1
 z=z+1
 k=k+1
 for c in range(z,row): 
 lis[b][c]=a
 a=a+1
 for d in range(x,row):
 lis[d][row-1]=a
 a=a+1
 for e in range(row-1,z,-1):
 lis[row-1][e-1]=a
 a=a+1
 for f in range(row-2,z,-1):
 lis[f][z]=a
 a=a+1
for i in range(0,y):
 print()
 for j in range(0,y):
 print(lis[i][j],end="\t")
Jan Kuiken
1,52311 silver badges11 bronze badges
asked Dec 23, 2019 at 5:13
\$\endgroup\$
6
  • 1
    \$\begingroup\$ What does the code do? \$\endgroup\$ Commented Dec 23, 2019 at 6:52
  • \$\begingroup\$ So an input of 3 produces a 3x3 matrix with the numbers 1 to 3². Is there anything significant about the ordering of the numbers? They don’t add up to a constant value, so it isn’t a magic square. At the risk of repeating myself, what does it do? Giving us an example of the output doesn’t tell us anything if we don’t understand what is significant about it. \$\endgroup\$ Commented Dec 24, 2019 at 4:07
  • \$\begingroup\$ Nice piece of code, but quite unreadable, could you change the variable names to names which reflect the meaning of the variables. Editing the question before there are answers is ok. \$\endgroup\$ Commented Dec 24, 2019 at 10:43
  • \$\begingroup\$ @JanKuiken THANK YOU SIR \$\endgroup\$ Commented Dec 24, 2019 at 12:25
  • \$\begingroup\$ Unrelated to the question, but some of your suggested edits are getting rejected. Quite a lot of them, actually. Please be more careful when suggesting an edit. Please make sure you're not introducing new mistakes, and changing British to American spelling is not considered polite. Thank you. \$\endgroup\$ Commented Mar 19, 2020 at 14:19

4 Answers 4

5
\$\begingroup\$

The code can be reduced by using:

row = int(input("Input number := "))
lis = [[0 for i in range(0,row)] for j in range(0,row)]
s = []
if row > 1:
 s += [row-1]
 for i in range(row-1, 0, -1):
 s += [i,i]
b = 1
e = 1
a = 0
c = 0
d = 0
lis[0][0] = e
for n in s:
 for f in range(n):
 c += a
 d += b
 e += 1
 lis[c][d] = e
 a, b = b, -a
for i in range(0,row):
 print()
 for j in range(0,row):
 print(lis[i][j],end="\t")

However this code is as unreadable as your code and probably uses a different method than yours. You do not only write code to perform a specific task, but you also want to communicate to other programmers (or yourself when you look at the code a year later) what you have done. This can be done by:

  • using sensible variable names
  • splitting up codes in smaller pieces (functions)
  • comments
  • docstrings

Your method is probably very clever, but I cannot figure out how it works from your code. My code can made be more understandable, although far from perfect, by applying previous points:

"""
Code to create a square matrix filled with ascending values in a inward
spiral starting from the upper left. The matrix is a list of lists.
Conventions used:
 i - first matrix index or row index
 j - second matrix index or column index
 di, dj - direction vector to move from one matrix position to another
"""
def rotate_90degrees_clockwise(di, dj):
 """Rotates a direction vector (di,dj) clockwise, i.e.:
 RIGHT(0,1) -> DOWN(1,0) -> LEFT(0,-1) -> UP(-1,0)
 """
 return dj, -di
def spiral_direction_steps(n):
 """Create a list of numbers of steps to go sequentially to the right, 
 down, left, up, right, down, left, up, ... etc.. to create a inward 
 spiraling route, starting from the upper left, i.e. for n = 3:
 2 x right, 2 x down, 2 x left, 1 x up, 1 x right
 General idea:
 1) first we go (n-1) x right, (n-1) x down, (n-1) x left
 2) then we go (n-2) x up, (n-2) x right
 3) then we go (n-3) x down, (n-3) x left
 4) repeat steps 2 and 3 till the number of steps is 1
 """
 retval = []
 if n > 1:
 retval += [n-1]
 for i in range(n-1, 0, -1):
 retval += [i,i]
 return retval
def spiral_matrix(n):
 """Generate a square matrix (list of lists) of size n x n, with ascending 
 numbers in a clockwise spiral, starting in the upper left corner
 """
 mat = [[0 for i in range(0,n)] for j in range(0,n)]
 val = 1 # start value
 i, j = 0, 0 # start point
 di, dj = 0, 1 # start direction 
 mat[i][j] = val # fill start point
 # fill other points
 steps = spiral_direction_steps(n)
 for n in steps:
 for _ in range(n):
 i += di
 j += dj
 val += 1
 mat[i][j] = val
 di, dj = rotate_90degrees_clockwise(di, dj)
 return mat
def print_matrix(mat):
 """Prints a matrix which is a list of lists"""
 for row in mat:
 print()
 for col in row:
 print(col, end="\t")
 print()
def main():
 n = int(input("Input number := "))
 matrix = spiral_matrix(n)
 print_matrix(matrix)
if __name__ == "__main__":
 main()
answered Dec 24, 2019 at 17:12
\$\endgroup\$
6
+25
\$\begingroup\$

Covering what others suggested (e.g. @Srivaths), please follow at least PEP8 Python PEP8

Then, put your code in some function. Give meaningful names to variables. (What's a, c, x, z ... why are you not using b? )

You don't need import math -- instead of f=row/2 f=math.ceil(f), you can do f = row // 2 (assuming you use python 3).

Note that you can solve the problem more generally, for m x n matrix, which you can initialize as: matrix = [[0 for col in range(nCols)] for row in range(nRows)] (see the answer from StackOverflow, provided by @OK). This matrix = [[0] * m] * n, as pointed in the comments, won't work because of list references.

Now, we can also observe that you can fill the "outer rectangle", i.e. matrix[0][:] = range(1, nCols + 1); then, you can fill the rightmost column

cnt += nCols
for row in range(1, nRows):
 matrix[row][rightIdx] = cnt
 cnt += 1
# Bottom row:
matrix[bottomIdx][leftIdx:] = reversed(range(cnt, cnt + nCols - 1) # might be off by 1;
# Complete first column
# Put this in a while loop;

This problem is similar -- once you have the matrix, print it in a spiral order: Geeks for geeks website.

Also, you can check the solution of LeetCode Prob 54, but I would encourage you to try solving the problem yourself first. Problem 54 Solution for Problem 54

And here is my solution to Problem 54, similar to Solution 2 (Leetcode Solution link above): My solution :)

answered Feb 22, 2020 at 20:47
\$\endgroup\$
2
  • 1
    \$\begingroup\$ As the function used here is math.ceil(row / 2), I believe row // 2 will floor the parameter. (row + 1) // 2 should work fine though. \$\endgroup\$ Commented Feb 22, 2020 at 23:05
  • 1
    \$\begingroup\$ Using lis as [[0] * m] * n would create some weird errors. The reference to [0] * m will be created n times. If I modify any index, all the values in the column will also be modified. \$\endgroup\$ Commented Feb 22, 2020 at 23:07
5
\$\begingroup\$

As @JanKuiken mentioned, your idea is probably clever, but I can't understand what your code does either! Please add it to the question if possible!

  • You need more spaces in your code!

  • Prefer += and -= operators as they are more compact than assignments such as x = x + 1.

  • for variable in range(0, end) is not necessary as range starts the sequence with 0 by default.

  • Use meaningful variable names

  • The variable y is declared unnecessarily.

a = 1
c = 0
z = 0
x = 1
k = 1
  • The above part looks pretty bad. Change it to the below code
c = z = 0
a = x = k = 1
  • The variable f outside the for loop is conflicting with the f inside the for loop. You can remove the use of f with for b in range(math.ceil(row / 2)):

  • lis = [[0] * row for j in range(row)] is faster!

  • To print the array, use

for i in lis: # Faster and smaller!
 print(*i, sep='\t')

Here's a glimpse of how your final code might look like:

import math
row = int(input("Input number := "))
lis = [[0] * row for j in range(row)]
c = z = 0
a = x = k = 1
for b in range(math.ceil(row / 2)):
 if b == k:
 row -= 1
 x += 1
 z += 1
 k += 1
 for c in range(z, row):
 lis[b][c] = a
 a += 1
 for d in range(x, row):
 lis[d][row-1] = a
 a += 1
 for e in range(row-1, z, -1):
 lis[row-1][e-1] = a
 a += 1
 for f in range(row-2, z, -1):
 lis[f][z] = a
 a += 1
for i in lis:
 print(*i, sep='\t')

Here's how I'd have approached this problem:

n = int(input('Enter the size of the grid: '))
result = [[0] * n for _ in range(n)]
# Ending points
ei, ej = n // 2, (n - 1) // 2
# 0: RIGHT, 1: DOWN, 2: LEFT, 3: UP
orient = 0
def fill(i: int, j: int, di: int, dj: int, val: int) -> tuple:
 """
 'i' is the current row index
 'j' is the current column index
 'di' is the direction of the row (1: UP, -1: DOWN)
 'dj' is the direction of the column (1: RIGHT, -1: LEFT)
 'val' is the next value in the spiral
 """
 while 0 <= i + di < n and 0 <= j + dj < n:
 if result[i + di][j + dj] != 0:
 break
 i += di
 j += dj
 result[i][j] = val
 val += 1
 return i, j, val
# 'j' is -1 because the (0, 0) is yet to be filled
i, j = 0, -1
val = 1
while (i, j) != (ei, ej):
 if orient == 0: i, j, val = fill(i, j, 0, 1, val)
 if orient == 1: i, j, val = fill(i, j, 1, 0, val)
 if orient == 2: i, j, val = fill(i, j, 0, -1, val)
 if orient == 3: i, j, val = fill(i, j, -1, 0, val)
 orient = (orient + 1) % 4
for i in result:
 print(*i, sep='\t')
answered Feb 21, 2020 at 19:02
\$\endgroup\$
2
\$\begingroup\$

I try to resolve the problem using recursion. It's not the most efficient solution in Python, but it's elegant and clean.

def spiral(mx, i, j, dir, a, max_i, max_j):
 """
 mx: matrix to fill
 i, j: matrix position to analize
 dir: direction to fill
 a: list of values to insert
 max_i, max_j: dimension of matrix
 """
 # no more value tu insert
 if len(a) == 0:
 # stop recursion
 return
 if dir == "right":
 if j < max_j and mx[i][j] == 0:
 mx[i][j] = a[0]
 spiral(mx, i, j+1, "right", a[1:], max_i, max_i)
 else:
 spiral(mx, i+1, j-1, "down", a, max_i, max_j)
 elif dir == "down":
 if i < max_i and mx[i][j] == 0:
 mx[i][j] = a[0]
 spiral(mx, i+1, j, "down", a[1:], max_i, max_j)
 else:
 spiral(mx, i-1, j-1, "left", a, max_i, max_j)
 elif dir == "left":
 if j >= 0 and mx[i][j] == 0:
 mx[i][j] = a[0]
 spiral(mx, i, j-1, "left", a[1:], max_i, max_j)
 else:
 spiral(mx, i-1, j+1, "up", a, max_i, max_j)
 elif dir == "up":
 if i >= 0 and mx[i][j] == 0:
 mx[i][j] = a[0]
 spiral(mx, i-1, j, "up", a[1:], max_i, max_j)
 else:
 spiral(mx, i+1, j+1, "right", a, max_i, max_j)
# square matrix dimesion
n_dim = 30
# list of values to insert in matrix
l = [x+1 for x in range(n_dim**2)]
# matrix to fill
mx = [[0 for i in range(n_dim)] for j in range(n_dim)]
# start recursion
spiral(mx, 0, 0, "right", l, n_dim, n_dim)
for i in range(n_dim):
 for j in range(n_dim):
 print("{0:4d}".format(mx[i][j]), end="")
 print("\n")
answered Feb 29, 2020 at 10:45
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.