How do I make make spiral in python? [closed]

微笑、不失礼 提交于 2019-12-18 11:37:21

问题


I want to make a function that I give it a number and the function returns a spiral from 1 to that number(in 2 dimensional array). For example if I give the number 25 to the function it will return something like this:


I tried different ways but nothing worked out. I just cant figure it out.
Hope I explained myself properly.

回答1:


Mostly the issue here is one of enumerating coordinates - match numbers to coordinates, then print it out however you want.

Start by noticing the two fundamental patterns:

  • (Direction) Move right, then down, then left, then up, then... (this is hopefully obvious)
  • (Magnitude) Move one, then one, then two, then two, then three...

So with those rules, write a generator that yields number, coordinates tuples.

It's clearest if you set up some helper functions first; I'll be extra verbose:

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]

Easy enough, now the generator:

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

demo:

list(gen_points(25))
Out[59]: 
[(1, (0, 0)),
 (2, (1, 0)),
 (3, (1, -1)),
 (4, (0, -1)),
 (5, (-1, -1)),
 (6, (-1, 0)),
 (7, (-1, 1)),
 (8, (0, 1)),
 (9, (1, 1)),
 (10, (2, 1)),
 (11, (2, 0)),
 (12, (2, -1)),
 (13, (2, -2)),
 (14, (1, -2)),
 (15, (0, -2)),
 (16, (-1, -2)),
 (17, (-2, -2)),
 (18, (-2, -1)),
 (19, (-2, 0)),
 (20, (-2, 1)),
 (21, (-2, 2)),
 (22, (-1, 2)),
 (23, (0, 2)),
 (24, (1, 2)),
 (25, (2, 2))]



回答2:


Here's a diagram to help you think about the problem:

You can think of this as repeatedly adding to an NxN square to make an (N+1)x(N+1) square:

if N is odd:
    move right one step
    move down N steps
    move left N steps
else:
    move left one step
    move up N steps
    move right N steps

and at each step you write a number to the current location.

As @Milan points out, you may not always want to complete the current shell (ie if you only want to count to 23). The easiest way to do this is to make a generator function which produces an endless stream of steps, then consume only as many steps as you need:

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

Before this can be used we have to decide how to store the values and, based on that, how to represent UP, DOWN, LEFT, and RIGHT.

The simplest storage is a 2d array, or in Python terms a list of lists. The outer list will hold rows of output and the inner lists will each hold cells within a row, and each cell can be addressed as my_array[y][x] with x increasing from left to right and y increasing from the top downward (this matches the order in which we expect to print output).

This allows us to define our directions:

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)

Before we can allocate storage, we need to know how big an array we need:

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)

I have added two extra pieces here: in order for the output to be tidy, every item should print the same width. output() is a function that takes a value and returns a string of the correct width, and EMPTY is a string of spaces of that width:

# 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("")

Now the pieces are in place, and we can generate the spiral:

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)

and print it out:

print("\n".join(" ".join(row) for row in square))

and it runs like:

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



回答3:


There are a few steps to the problem. First, set up a grid. The size of the grid will need to be equal to the next higher perfect square; for example, if you enter 23, you need a 5 × 5 (25) grid, or if you enter 31 you'll need a 6 × 6 grid (36). Next, store the next value of the number sequence in a "current position" (i.e., the center). At each step, check the cardinal directions and move the "current position" to the location which has not previously been filled in which is closest to the center, with a bias towards east (to deal with the initial step, where there is no difference in N, S, E, W). Proceed until your iterator is finished.


Edit: I really enjoyed this question, so I went to write a nice solution. It's been a bit since I wrote Python, so this may not be the most elegant, but nevertheless.

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"

Example output:

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 



回答4:


so I assume you can already somehow determine the size of the array and the position of the "one". now you need a function that allows you to change direction and a counter.

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++

this is just a sketch. what is still missing (but easy to figure out):

  • how to implement the functions
  • how to set multiple numbers while following one direction


来源:https://stackoverflow.com/questions/23706690/how-do-i-make-make-spiral-in-python

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!