Как сделать make спираль в python?

Я хочу сделать функцию, которую я даю ей число, и функция возвращает спираль от 1 до этого числа (в 2-мерном массиве). Например, если я дам номер 25 функции, он вернет что-то вроде этого:
введите описание изображения здесь
Я пробовал разные способы, но ничего не получилось. Я просто не могу понять это.
Надеюсь, я правильно объяснил.

4 Solutions collect form web for “Как сделать make спираль в python?”

В основном проблема здесь – это перечисление координат – сопоставление чисел с координатами, а затем распечатка, но вы хотите.

Начните с замечания двух основных шаблонов:

  • (Направление) Двигайтесь вправо, затем вниз, затем влево, затем вверх, затем … (это, надеюсь, очевидно)
  • (Величина) Перемещайте один, затем один, затем два, затем два, затем три …

Таким образом, с этими правилами напишите генератор, который дает number, coordinates кортежи.

Это ясно, если вы сначала настроили некоторые вспомогательные функции; Я буду дополнительным подробным:

 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 

демо:

 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))] 

Есть несколько шагов к проблеме. Сначала настройте сетку. Размер сетки должен быть равен следующему высшему совершенному квадрату; например, если вы вводите 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 

Вот диаграмма, которая поможет вам подумать о проблеме:

введите описание изображения здесь

Вы можете думать об этом как о многократном добавлении к квадрату NxN, чтобы сделать квадрат (N + 1) x (N + 1):

 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 

и на каждом шаге вы записываете номер в текущее местоположение.

Как указывает @Milan, вы не всегда можете завершить текущую оболочку (то есть, если хотите только подсчитать до 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 

Прежде чем это можно будет использовать, мы должны решить, как сохранить значения, и на основе этого, как представлять UP , DOWN , LEFT и 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 

поэтому я предполагаю, что вы уже можете как-то определить размер массива и положение «одного». теперь вам нужна функция, которая позволяет вам менять направление и счетчик.

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

это всего лишь эскиз. что все еще отсутствует (но легко понять):

  • как реализовать функции
  • как установить несколько номеров, следуя одному направлению
Python - лучший язык программирования в мире.