Last active
April 9, 2021 08:31
-
-
Save tomofuminijo/643b529365dc458ff34f93381cca3af8 to your computer and use it in GitHub Desktop.
Sample code to output Ulam spiral image in Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from PIL import Image | |
from sympy import isprime | |
import math | |
def ulam_spiral(length: int, start_num: int = 1) -> list[list[int]]: | |
""" | |
ulam_spiral plots the spiral of Ulam on a square with a side length of | |
the inputed length. | |
The result is returned in the coordinates of a two-dimensional array. | |
Prime numbers are set to 1 and non-prime numbers are set to 0. | |
Parameters | |
---------- | |
length : int | |
The length of one side of the ulam spiral to be created. | |
start_num : int, optional | |
Starting number of Ulam spiral, default is 1 | |
Returns | |
------- | |
list[list[int]] | |
A two-dimensional array of prime numbers plotted on a Ulam spiral | |
with the length on one side. | |
Set to 1 for prime numbers and 0 for non-prime numbers. | |
""" | |
# Ulam spiral container, length * length, all initialized to 0 | |
spiral = [[0 for x in range(length)] for i in range(length)] | |
# Center point of the spiral (odd numbers will be moved up) | |
start_x = math.ceil(length/2) | |
start_y = math.ceil(length/2) | |
# Position of the spiral | |
spiral_pos = SpiralPosition(start_x, start_y) | |
# Number to be checked | |
number = start_num | |
for i in range(length*length): | |
x, y = spiral_pos.next() | |
if any([x < 0, y < 0, x >= length, y >= length]): | |
continue | |
if isprime(number): | |
spiral[x][y] = 1 | |
number += 1 | |
return spiral | |
class Direction(): | |
""" | |
Direction keeps track of the direction the spiral is moving on and | |
the number of move. | |
""" | |
# RIGHT: x+ | |
RIGHT = (1, 0) | |
# UP: y- | |
UP = (0, -1) | |
# LEFT: x- | |
LEFT = (-1, 0) | |
# DOWN: y+ | |
DOWN = (0, 1) | |
def __init__(self) -> None: | |
self._direction = [] | |
self._cycle = 0 | |
def next_directions(self) -> list[tuple[int]]: | |
""" | |
Returns the next direction in the spiral as an array of movement counts. | |
Returns | |
------- | |
list[tupple[int]] | |
List with the next direction arrayed by the number of moves. | |
""" | |
if not self._direction: | |
self._create_direction_list() | |
direction = self._direction.pop(0) | |
# right/up move count increase 1, 3, 5, 7, ... per cycle | |
# left/down move count increase 2, 4, 6, 8, ... per cycle | |
if direction in [Direction.RIGHT, Direction.UP]: | |
move_count = self._cycle*2 - 1 | |
else: | |
move_count = self._cycle*2 | |
next_directions = [direction for i in range(move_count)] | |
return next_directions | |
def _create_direction_list(self): | |
self._direction = [Direction.RIGHT, | |
Direction.UP, | |
Direction.LEFT, | |
Direction.DOWN] | |
self._cycle += 1 | |
class SpiralPosition: | |
""" | |
SpiralPosition manages the current position of the spiral and | |
returns the next position. | |
""" | |
def __init__(self, start_x: int = 0, start_y: int = 0) -> None: | |
""" | |
When instantiating SpiralPosition, pass the starting position of | |
the spiral. | |
Parameters | |
---------- | |
start_x : int, optional | |
Start x-position of the spiral, default is 0 | |
start_y : int, optional | |
Start y position of the spiral, default is 0 | |
""" | |
self.current_pos = [start_x, start_y] | |
self.direction = Direction() | |
self.side_position_list = [] | |
def next(self) -> tuple[int, int]: | |
""" | |
Returns the next position on the spiral. | |
Returns | |
------- | |
tupple(int, int) | |
next position(x, y) on the spiral | |
""" | |
if not self.side_position_list: | |
self._create_next_side_positions() | |
cp = self.side_position_list.pop(0) | |
return cp[0], cp[1] | |
def _create_next_side_positions(self): | |
# Get next directions and create next positions | |
next_directions = self.direction.next_directions() | |
for d in next_directions: | |
self.current_pos = [x + y | |
for x, y | |
in zip(self.current_pos, d)] | |
self.side_position_list.append(self.current_pos) | |
if __name__ == '__main__': | |
SIDE_LENGTH = 200 | |
BLACK = (0, 0, 0) | |
WHITE = (255, 255, 255) | |
# Generate an array of Ulam spiral | |
ulam = ulam_spiral(SIDE_LENGTH) | |
# Drawing ural spiral on an image | |
image = Image.new('RGB', (SIDE_LENGTH, SIDE_LENGTH), BLACK) | |
for x in range(len(ulam)): | |
for y in range(len(ulam[x])): | |
if ulam[x][y]: | |
image.putpixel( | |
(x, y), | |
WHITE | |
) | |
image.save('ulam_spiral.png') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment