Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

python

spiral

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:
enter image description here
I tried different ways but nothing worked out. I just cant figure it out.
Hope I explained myself properly.

like image 300
Skizo Avatar asked May 17 '14 01:05

Skizo


People also ask

How do you finish a turtle python?

turtle. bye() , aka turtle. Screen(). bye() , closes a turtle graphics window.


2 Answers

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))]
like image 121
roippi Avatar answered Nov 14 '22 21:11

roippi


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

enter image description here

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
like image 21
Hugh Bothwell Avatar answered Nov 14 '22 22:11

Hugh Bothwell