我和我的伙伴正试图在python中创建一个有趣的游戏,其中输入数组的元素以螺旋方式访问。我尝试了一些方法,如下面给出的方法({*})。
def spiral(X, Y):
x = y = 0
dx = 0
dy = -1
for i in range(max(X, Y)**2):
if (-X/2 < x <= X/2) and (-Y/2 < y <= Y/2):
print (x, y)
# DO STUFF...
if x == y or (x < 0 and x == -y) or (x > 0 and x == 1-y):
dx, dy = -dy, dx
x, y = x+dx, y+dy
上述语句访问螺旋循环中的元素并为定义的数组AE打印它们。我想知道如何将给定的阵列AE转换为螺旋阵列
答案 0 :(得分:12)
您可以通过在矩阵中心附近开始构建螺旋线,并始终向右转,除非已经访问过该元素:
#!/usr/bin/env python
NORTH, S, W, E = (0, -1), (0, 1), (-1, 0), (1, 0) # directions
turn_right = {NORTH: E, E: S, S: W, W: NORTH} # old -> new direction
def spiral(width, height):
if width < 1 or height < 1:
raise ValueError
x, y = width // 2, height // 2 # start near the center
dx, dy = NORTH # initial direction
matrix = [[None] * width for _ in range(height)]
count = 0
while True:
count += 1
matrix[y][x] = count # visit
# try to turn right
new_dx, new_dy = turn_right[dx,dy]
new_x, new_y = x + new_dx, y + new_dy
if (0 <= new_x < width and 0 <= new_y < height and
matrix[new_y][new_x] is None): # can turn right
x, y = new_x, new_y
dx, dy = new_dx, new_dy
else: # try to move straight
x, y = x + dx, y + dy
if not (0 <= x < width and 0 <= y < height):
return matrix # nowhere to go
def print_matrix(matrix):
width = len(str(max(el for row in matrix for el in row if el is not None)))
fmt = "{:0%dd}" % width
for row in matrix:
print(" ".join("_"*width if el is None else fmt.format(el) for el in row))
示例:
>>> print_matrix(spiral(5, 5))
21 22 23 24 25
20 07 08 09 10
19 06 01 02 11
18 05 04 03 12
17 16 15 14 13
答案 1 :(得分:7)
该问题与以螺旋顺序打印阵列的问题密切相关。事实上,如果我们已经有了一个功能,那么问题就相对简单了。
how to produce a spiral matrix上有多种资源,或者如何以螺旋顺序排列loop或print数组。即便如此,我决定使用numpy数组编写自己的版本。这个想法不是原创的,但使用numpy会使代码更简洁。
另一个原因是,我发现的大多数生成螺旋矩阵的例子(包括问题和其他答案中的代码)仅涉及奇数n的大小为n x n的平方矩阵。在其他尺寸的矩阵中找到起点(或终点)可能很棘手。例如,对于3x5矩阵,它不能是中间单元。下面的代码是通用的,起始(结束)点的位置取决于函数spiral_xxx
的选择。
第一个函数以顺时针螺旋顺序展开数组:
import numpy as np
def spiral_cw(A):
A = np.array(A)
out = []
while(A.size):
out.append(A[0]) # take first row
A = A[1:].T[::-1] # cut off first row and rotate counterclockwise
return np.concatenate(out)
我们可以根据我们开始的位置以及如何旋转矩阵,以八种不同的方式编写此函数。我将给出另一个与问题中的图像中的矩阵变换一致的(后面将会很明显)。所以,进一步说,我将使用这个版本:
def spiral_ccw(A):
A = np.array(A)
out = []
while(A.size):
out.append(A[0][::-1]) # first row reversed
A = A[1:][::-1].T # cut off first row and rotate clockwise
return np.concatenate(out)
工作原理:
A = np.arange(15).reshape(3,5)
print(A)
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
print(spiral_ccw(A))
[ 4 3 2 1 0 5 10 11 12 13 14 9 8 7 6]
请注意,结束(或开始)点不是中间单元格。此函数适用于所有类型的矩阵,但我们需要一个生成螺旋索引的辅助函数:
def base_spiral(nrow, ncol):
return spiral_ccw(np.arange(nrow*ncol).reshape(nrow,ncol))[::-1]
例如:
print(base_spiral(3,5))
[ 6 7 8 9 14 13 12 11 10 5 0 1 2 3 4]
现在来两个主要功能。一个将矩阵转换为相同维度的螺旋形式,另一个转换为转换:
def to_spiral(A):
A = np.array(A)
B = np.empty_like(A)
B.flat[base_spiral(*A.shape)] = A.flat
return B
def from_spiral(A):
A = np.array(A)
return A.flat[base_spiral(*A.shape)].reshape(A.shape)
Matrix 3 x 5:
A = np.arange(15).reshape(3,5)
print(A)
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
print(to_spiral(A))
[[10 11 12 13 14]
[ 9 0 1 2 3]
[ 8 7 6 5 4]]
print(from_spiral(to_spiral(A)))
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
来自问题的矩阵:
B = np.arange(1,26).reshape(5,5)
print(B)
[[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[21 22 23 24 25]]
print(to_spiral(B))
[[21 22 23 24 25]
[20 7 8 9 10]
[19 6 1 2 11]
[18 5 4 3 12]
[17 16 15 14 13]]
print(from_spiral(to_spiral(B)))
[[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[21 22 23 24 25]]
如果你只打算使用固定大小的矩阵,例如5x5,那么用固定的索引矩阵替换函数定义中的base_spiral(*A.shape)
是值得的,比如Ind
(Ind = base_spiral(5,5)
)。
答案 2 :(得分:2)
这是一个使用itertools
并且几乎没有数学的解决方案,只是观察螺旋的样子。我觉得它很优雅,很容易理解。
from math import ceil, sqrt
from itertools import cycle, count, izip
def spiral_distances():
"""
Yields 1, 1, 2, 2, 3, 3, ...
"""
for distance in count(1):
for _ in (0, 1):
yield distance
def clockwise_directions():
"""
Yields right, down, left, up, right, down, left, up, right, ...
"""
left = (-1, 0)
right = (1, 0)
up = (0, -1)
down = (0, 1)
return cycle((right, down, left, up))
def spiral_movements():
"""
Yields each individual movement to make a spiral:
right, down, left, left, up, up, right, right, right, down, down, down, ...
"""
for distance, direction in izip(spiral_distances(), clockwise_directions()):
for _ in range(distance):
yield direction
def square(width):
"""
Returns a width x width 2D list filled with Nones
"""
return [[None] * width for _ in range(width)]
def spiral(inp):
width = int(ceil(sqrt(len(inp))))
result = square(width)
x = width // 2
y = width // 2
for value, movement in izip(inp, spiral_movements()):
result[y][x] = value
dx, dy = movement
x += dx
y += dy
return result
用法:
from pprint import pprint
pprint(spiral(range(1, 26)))
输出:
[[21, 22, 23, 24, 25],
[20, 7, 8, 9, 10],
[19, 6, 1, 2, 11],
[18, 5, 4, 3, 12],
[17, 16, 15, 14, 13]]
以下是缩短的相同解决方案:
def stretch(items, counts):
for item, count in izip(items, counts):
for _ in range(count):
yield item
def spiral(inp):
width = int(ceil(sqrt(len(inp))))
result = [[None] * width for _ in range(width)]
x = width // 2
y = width // 2
for value, (dx, dy) in izip(inp,
stretch(cycle([(1, 0), (0, 1), (-1, 0), (0, -1)]),
stretch(count(1),
repeat(2)))):
result[y][x] = value
x += dx
y += dy
return result
我忽略了你希望输入成为2D数组这一事实,因为它更适合于任何一维迭代。如果需要,您可以轻松地展平输入的2D阵列。我也假设输出应该是一个正方形,因为我不能想到你明智的想法。如果方块长度均匀且输入太长,它可能会越过边缘并引发错误:再次,我不知道替代方案是什么。
答案 3 :(得分:1)
下面是变换的python3代码:
[[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]]
到
[[20, 19, 18, 17, 16],
[21, 6, 5, 4, 15],
[22, 7, 0, 3, 14],
[23, 8, 1, 2, 13],
[24, 9, 10, 11, 12]]
您可以通过这种方式轻松更改实施方式......
def spiral(X, Y):
x = y = 0
dx = 0
dy = -1
for i in range(max(X, Y) ** 2):
if (-X / 2 < x <= X / 2) and (-Y / 2 < y <= Y / 2):
yield x, y
# print(x, y)
# DO STUFF...
if x == y or (x < 0 and x == -y) or (x > 0 and x == 1 - y):
dx, dy = -dy, dx
x, y = x + dx, y + dy
spiral_matrix_size = 5
my_list = list(range(spiral_matrix_size**2))
my_list = [my_list[x:x + spiral_matrix_size] for x in range(0, len(my_list), spiral_matrix_size)]
print(my_list)
for i, (x, y) in enumerate(spiral(spiral_matrix_size, spiral_matrix_size)):
diff = int(spiral_matrix_size / 2)
my_list[x + diff][y + diff] = i
print(my_list)
答案 4 :(得分:0)
你可以像这样填充数组:
#!/usr/bin/python
class filler:
def __init__(self, srcarray):
self.size = len(srcarray)
self.array = [[None for y in range(self.size)] for y in range(self.size)]
self.xpos, self.ypos = 0, 0
self.directions = [self.down, self.right, self.up, self.left]
self.direction = 0
self.fill(srcarray)
def fill(self, srcarray):
for row in reversed(srcarray):
for elem in reversed(row):
self.array[self.xpos][self.ypos] = elem
self.go_to_next()
def check_next_pos(self):
np = self.get_next_pos()
if np[1] in range(self.size) and np[0] in range(self.size):
return self.array[np[0]][np[1]] == None
return False
def go_to_next(self):
i = 0
while not self.check_next_pos() and i < 4:
self.direction = (self.direction + 1) % 4
i += 4
self.xpos, self.ypos = self.get_next_pos()
def get_next_pos(self):
return self.directions[self.direction](self.xpos, self.ypos)
def down(self, x, y):
return x + 1, y
def right(self, x, y):
return x, y + 1
def up(self, x, y):
return x - 1, y
def left(self, x, y):
return x, y - 1
def print_grid(self):
for row in self.array:
print(row)
f = filler([[x+y*5 for x in range(5)] for y in range(5)])
f.print_grid()
输出结果如下:
[24, 9, 10, 11, 12]
[23, 8, 1, 2, 13]
[22, 7, 0, 3, 14]
[21, 6, 5, 4, 15]
[20, 19, 18, 17, 16]
答案 5 :(得分:0)
def counter(n):
for i in range(1,n*n):
yield i+1
n = 11
a = [[1 for x in range(n)] for y in range(n)]
x = y = n//2
val = counter(n)
for i in range(2, n, 2):
y += 1
x -= 1
for k in range(i):
x += 1
a[x][y] = next(val)
for k in range(i):
y -= 1
a[x][y] = next(val)
for k in range(i):
x -= 1
a[x][y] = next(val)
for k in range(i):
y += 1
a[x][y] = next(val)
for i in range(n):
for j in range(n):
print (a[i][j] , end="")
print (" " , end="")
print("\n")
答案 6 :(得分:0)
我正在做一些有关生成数组的各种螺旋索引的事情,并且我对answer of ptrj添加了一些简单的修改以使函数更通用。修改后的功能支持从四个角开始按顺时针和逆时针方向开始索引。
def spiral_ind(A,start,direction):
if direction == 'cw':
if start == 'right top':
A = np.rot90(A)
elif start == 'left bottom':
A = np.rot90(A,k=3)
elif start == 'right bottom':
A = np.rot90(A,k=2)
elif direction == 'ccw':
if start == 'left top':
A = np.rot90(A,k=3)
elif start == 'left bottom':
A = np.rot90(A,k=2)
elif start == 'right bottom':
A = np.rot90(A)
out = []
while(A.size):
if direction == 'cw':
out.append(A[0])
A = A[1:].T[::-1]
elif direction == 'ccw':
out.append(A[0][::-1])
A = A[1:][::-1].T
return np.concatenate(out)
答案 7 :(得分:0)
def spiral(m):
a=[]
t=list(zip(*m)) # you get the columns by zip function
while m!=[]:
if m==[]:
break
m=list(zip(*t)) # zip t will give you same m matrix. It is necessary for iteration
a.extend(m.pop(0)) # Step1 : pop first row
if m==[]:
break
t=list(zip(*m))
a.extend(t.pop(-1)) # Step 2: pop last column
if m==[]:
break
m=list(zip(*t))
a.extend(m.pop(-1)[::-1]) # Step 3: pop last row in reverse order
if m==[]:
break
t=list(zip(*m))
a.extend(t.pop(0)[::-1]) # Step 4: pop first column in reverse order
return a
这个解是O(n);只是一个while循环;更快,可用于更大尺寸的矩阵