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")
-
1\$\begingroup\$ What does the code do? \$\endgroup\$AJNeufeld– AJNeufeld2019年12月23日 06:52:12 +00:00Commented 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\$AJNeufeld– AJNeufeld2019年12月24日 04:07:12 +00:00Commented 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\$Jan Kuiken– Jan Kuiken2019年12月24日 10:43:27 +00:00Commented Dec 24, 2019 at 10:43
-
\$\begingroup\$ @JanKuiken THANK YOU SIR \$\endgroup\$Brijesh Kalkani– Brijesh Kalkani2019年12月24日 12:25:20 +00:00Commented 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\$Mast– Mast ♦2020年03月19日 14:19:40 +00:00Commented Mar 19, 2020 at 14:19
4 Answers 4
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()
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 :)
-
1\$\begingroup\$ As the function used here is
math.ceil(row / 2)
, I believerow // 2
will floor the parameter.(row + 1) // 2
should work fine though. \$\endgroup\$Sriv– Sriv2020年02月22日 23:05:13 +00:00Commented 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 createdn
times. If I modify any index, all the values in the column will also be modified. \$\endgroup\$Sriv– Sriv2020年02月22日 23:07:47 +00:00Commented Feb 22, 2020 at 23:07
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 asx = x + 1
.for variable in range(0, end)
is not necessary asrange
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 thefor
loop is conflicting with thef
inside the for loop. You can remove the use off
withfor 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')
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")