如何在python中生成螺旋?

如何在python中生成螺旋?,python,spiral,Python,Spiral,我想做一个函数,我给它一个数字,这个函数返回一个从1到那个数字的螺旋线(在二维数组中)。例如,如果我给函数指定数字25,它将返回如下内容: 我尝试了不同的方法,但没有成功。我就是想不出来。 希望我能正确地解释自己。解决这个问题有几个步骤。首先,建立一个网格。网格的大小需要等于下一个更高的完美正方形;例如,如果输入23,则需要5×5(25)网格;如果输入31,则需要6×6网格(36)。接下来,将数字序列的下一个值存储在“当前位置”(即,中心)。在每一步中,检查基本方向,并将“当前位置”移动到之前

我想做一个函数,我给它一个数字,这个函数返回一个从1到那个数字的螺旋线(在二维数组中)。例如,如果我给函数指定数字25,它将返回如下内容:

我尝试了不同的方法,但没有成功。我就是想不出来。

希望我能正确地解释自己。

解决这个问题有几个步骤。首先,建立一个网格。网格的大小需要等于下一个更高的完美正方形;例如,如果输入23,则需要5×5(25)网格;如果输入31,则需要6×6网格(36)。接下来,将数字序列的下一个值存储在“当前位置”(即,中心)。在每一步中,检查基本方向,并将“当前位置”移动到之前未填充的位置,该位置最靠近中心,偏向东方(处理初始步骤,其中N、S、E、W没有差异)。继续,直到迭代器完成


编辑:我真的很喜欢这个问题,所以我去写了一个很好的解决方案。我写Python已经有一段时间了,所以这可能不是最优雅的,但无论如何

from functools import partial
from math import ceil, sqrt

def gen_grid(n):
  grid_size = int(ceil(sqrt(n)))
  return [[None for _ in range(grid_size)] for _ in range(grid_size)]

def valid_coord(grid, coord):
  try:
    return grid[coord[0]][coord[1]] is None
  except:
    return False

def origin(size):
  adjustment = 1 if size % 2 == 0 else 0
  return (size / 2 - adjustment), (size / 2 - adjustment)

north = lambda y, x: (y - 1, x)
south = lambda y, x: (y + 1, x)
east = lambda y, x: (y, x + 1)
west = lambda y, x: (y, x - 1)

directions = lambda y, x: [east(y, x), south(y, x), west(y, x), north(y, x)]
distance = lambda c, nxt: sqrt((c[0] - nxt[0]) ** 2 + (c[1] - nxt[1]) ** 2)

def walk_grid(nums):
  grid = gen_grid(len(nums))
  center = origin(len(grid[0]))
  current_position = center
  center_distance = partial(distance, center)

  for n in nums:
    y, x = current_position
    grid[y][x] = n
    unseen_points = [c for c in directions(y, x) if valid_coord(grid, c)]
    if n != nums[-1]:
      current_position = sorted(unseen_points, key=center_distance)[0]

  return grid

def print_grid(highest):
  result = walk_grid(range(1, highest + 1))
  for row in result:
    for col in row:
      print "{:>4}".format(col if col is not None else ''),  
    print "\n"
示例输出:

In [2]: grid.print_grid(25)
  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 get_new_direction(direction):
  switch direction:
     case E: return S
     case S: return W
     case W: return N
     case N: return E

i,j = initial_coordinates_of_the_one
direction = right
steps = 1
next_number = 1

while not done:
  place(next_number, i, j)
  i,j = get_coordinates_after_move(direction, steps)
  direction = get_new_direction(direction)
  next_number++
  if iteration is even:
    steps++
这只是个草图。还缺少什么(但很容易找到):

  • 如何实现这些功能
  • 如何在遵循一个方向的同时设置多个数字

这里的主要问题是枚举坐标-将数字与坐标匹配,然后根据需要打印出来

首先注意两种基本模式:

  • (方向)向右移动,然后向下,然后向左,然后向上,然后。。。(希望这是显而易见的)
  • (震级)移动一、一、二、二、三
根据这些规则,编写一个生成器,生成
number,坐标
元组

如果你先设置一些助手函数,这是最清楚的;我会非常详细:

def move_right(x,y):
    return x+1, y

def move_down(x,y):
    return x,y-1

def move_left(x,y):
    return x-1,y

def move_up(x,y):
    return x,y+1

moves = [move_right, move_down, move_left, move_up]
很简单,现在发电机:

def gen_points(end):
    from itertools import cycle
    _moves = cycle(moves)
    n = 1
    pos = 0,0
    times_to_move = 1

    yield n,pos

    while True:
        for _ in range(2):
            move = next(_moves)
            for _ in range(times_to_move):
                if n >= end:
                    return
                pos = move(*pos)
                n+=1
                yield n,pos

        times_to_move+=1
演示:


下面是一张帮助您思考问题的图表:

您可以将其视为重复添加到NxN正方形以生成(N+1)x(N+1)正方形:

在每一步中,您都会在当前位置写入一个数字

正如@Milan指出的,你可能并不总是想完成当前的shell(即如果你只想数到23)。最简单的方法是生成一个生成函数,该函数产生无穷多的步骤,然后只消耗所需的步骤:

from itertools import count

def steps_from_center():
    for n in count(start=1):
        if n % 2:
            yield RIGHT
            for i in range(n):
                yield DOWN
            for i in range(n):
                yield LEFT
        else:
            yield LEFT
            for i in range(n):
                yield UP
            for i in range(n):
                yield RIGHT
在使用之前,我们必须决定如何存储值,并在此基础上决定如何表示
向上
向下
,和

最简单的存储是2d数组,或者用Python术语来说是列表列表列表。外部列表将保存输出行,内部列表将保存一行中的每个单元格,每个单元格都可以称为
my_array[y][x]
,x从左向右递增,y从上向下递增(这与我们希望打印输出的顺序相匹配)

这使我们能够确定我们的方向:

from collections import namedtuple

Step  = namedtuple("Step", ["dx", "dy"])
RIGHT = Step( 1,  0)
DOWN  = Step( 0,  1)
LEFT  = Step(-1,  0)
UP    = Step( 0, -1)
在分配存储之前,我们需要知道需要多大的阵列:

from math import ceil, floor, log10, sqrt

max_i = int(input("What number do you want to display up to? "))

# how big does the square have to be?
max_n = int(ceil(sqrt(max_i)))

# here is our initialized data structure
square = [[EMPTY] * max_n for _ in range(max_n)]

# and we start by placing a 1 in the center:
x = y = max_n // 2
square[y][x] = output(1)
我在这里添加了两个额外的部分:为了使输出整洁,每个项目都应该打印相同的宽度
output()
是一个函数,它接受一个值并返回一个正确宽度的字符串,
EMPTY
是一个该宽度的空格字符串:

# how many digits in the largest number?
max_i_width = int(floor(log10(max_i))) + 1

# custom output formatter - make every item the same width
def output(item, format_string="{{:>{}}}".format(max_i_width)):
    return format_string.format(item)

EMPTY = output("")
现在各部分已就位,我们可以生成螺旋:

for i, step in enumerate(steps_from_center(), start=2):
    if i > max_i:
        break
    else:
        x += step.dx
        y += step.dy
        square[y][x] = output(i)
并打印出来:

print("\n".join(" ".join(row) for row in square))
它的运行方式如下:

What number do you want to display up to? 79
73 74 75 76 77 78 79      
72 43 44 45 46 47 48 49 50
71 42 21 22 23 24 25 26 51
70 41 20  7  8  9 10 27 52
69 40 19  6  1  2 11 28 53
68 39 18  5  4  3 12 29 54
67 38 17 16 15 14 13 30 55
66 37 36 35 34 33 32 31 56
65 64 63 62 61 60 59 58 57

听起来像是家庭作业——请把你试过的东西贴出来,即使它不起作用。这真的不是家庭作业。。只是为了好玩…数字可以是任意的?比如说,如果你给23分,听起来像是在节目采访中提出的问题的变体。我记得“打印任意矩形矩阵的所有对角线遍历”与此类似。相关:Python也没有
switch
语句或操作符
++
@davidhm这显然是伪代码。遗憾的是,这个问题已经解决了,但是我用numpy和一个定制的Vec2类编写了一个解决方案,在这个类中,你有一个点和一个方向向量,你可以添加它们。当你到达数组的末尾时,旋转90度:
direction=Vec2(-direction.y,direction.x)
其中direction被初始化为
Vec2(1,0)
读起来很有趣!(我的螺旋代码从左上角开始,一直到中间,很容易将生成的列表反转到里面!)
print("\n".join(" ".join(row) for row in square))
What number do you want to display up to? 79
73 74 75 76 77 78 79      
72 43 44 45 46 47 48 49 50
71 42 21 22 23 24 25 26 51
70 41 20  7  8  9 10 27 52
69 40 19  6  1  2 11 28 53
68 39 18  5  4  3 12 29 54
67 38 17 16 15 14 13 30 55
66 37 36 35 34 33 32 31 56
65 64 63 62 61 60 59 58 57