7

I was watching 3Blue1Brown's YouTube video "Why do prime numbers make these spirals?", and it inspired me to look for some patterns myself. So I made some Python code as follows below. How it works is that each nonnegative integer is placed along a spiral, with x(n) = n * cos(n) and y(n) = n * sin(n). Only numbers that are palindromes in the chosen base (original question is base 10) are displayed on the spiral, every other number is hidden.

#!/usr/bin/env python3

from typing import Generator import argparse import math

from PIL import Image, ImageDraw

def to_base(n: int, base: int) -> list[int]: """Converts a number to a base other than 10 Maximum base: 36 """ if base < 2 or base > 36: raise ValueError("Base must be greater than 1 and less than 36")

digits = []
while n &gt;= base:
    n, m = divmod(n, base)
    digits.append(m)
digits.append(n)
return digits


def palindromes(base: int = 10) -> Generator[int, None, None]: def is_palindrome(n: int): sn = to_base(n, base) return sn == sn[::-1]

n = 1
while True:
    if is_palindrome(n):
        yield n
    n += 1


def spiral_coords( n: float, x_origin: float = 0.0, y_origin: float = 0.0, scale: float = 1.0 ) -> tuple[float, float]: x = n * math.cos(n) y = n * math.sin(n) return x * scale + x_origin, y * scale + y_origin

def fill_spiral( gen: Generator[int, None, None], height: int, width: int, radius: float = 1.0, scale: float = 1.0, ): """Fills an Image of size (height, width) with zeroes. Then, fills each element of gen in on the spiral """ x_origin = width / 2 y_origin = height / 2 img = Image.new("1", (width, height), 0) draw = ImageDraw.Draw(img) for n in gen: x, y = spiral_coords(n, x_origin, y_origin, scale) # if either x or y is in bounds, draw circle if 0 <= x < width or 0 <= y < height: top_left = (x - radius, y - radius) bottom_right = (x + radius, y + radius) draw.ellipse([top_left, bottom_right], 1) else: break return img

def main(): parser = argparse.ArgumentParser(description="Create an image of the prime spiral") parser.add_argument("height", type=int, help="Image height") parser.add_argument("width", type=int, help="Image width") parser.add_argument( "--radius", type=float, help="Radius of each point, default 1", default=1.0 ) parser.add_argument("--base", type=int, help="Number base, default 10", default=10) parser.add_argument( "--scale", type=float, help="Zoom scale. Larger means more zoomed in, smaller is more zoomed out. Default 1", default=1.0, ) args = parser.parse_args()

spiral = fill_spiral(
    palindromes(args.base), args.height, args.width, args.radius, args.scale
)

spiral.show()


if name == "main": main()

And I ran the code as: ./main.py 10000 10000 --radius 10, receiving this image.

Outside of the dense cloud of palindromes in the middle, there is nothing but empty space and these arcs of palindromes. Some observations:

  • The spiral runs counterclockwise but these arcs run clockwise
  • Each arc is five palindromes long
  • Each arc appears to be placed along one of four clockwise spiral arms out of the central cloud

If anyone could make a larger image including more data points that would also be welcome. My image viewer apparently likes to crap out if I make the image any larger than 10000x10000 pixels, so I'm not getting as much data as I would like.

Edit: a comment asked about the number base. The displayed image is in base 10, but changing the base creates different patterns. For instance, base 5base 5, and base 16base 16

Lemma
  • 89
  • 3
  • 4
    Can you provide some details as to what condition defines your prime spiral? Having to extract this from the python code makes this rather inaccessible. – Semiclassical May 13 '23 at 17:22
  • @Semiclassical Yeah sorry about that. In the video (and my code), each nonnegative integer is placed along at coordinates (n * cos(n), n * sin(n)). This forms a spiral. In the video the "prime spiral" is this spiral filtered to only show prime numbers. I've done the same thing but filtered to only show palindromes. Hope that clarifies things. – Lemma May 13 '23 at 17:27
  • @Lemma And does "palindrome" means palindrome in base 10? – Milten May 13 '23 at 17:35
  • @Milten Yes it does – Lemma May 13 '23 at 17:42
  • It might be interesting to experiment with what happens in other bases. – Semiclassical May 13 '23 at 19:08
  • 2
    Rather than posting the code you should state where you plot $n$. It will be the key to explaining what is happening. – Ross Millikan May 14 '23 at 00:28
  • Ulam's spiral is the search of a pattern of the primes numbers that does not exist , entertaining perhaps , but without any real merit. – Peter Jun 18 '23 at 16:26

2 Answers2

6

All your points are on the Archimedean spiral that comes from polar plotting $(t,t)$. At the scale you are plotting that spiral is too tight to notice. I plotted the four digit palindromes in a spreadsheet and show the result below. I believe your series of five come from these. The cause of the groups of five is that the palindromes come in groups of $10$ at intervals of $110$. The first group of $10$ starts at $1001$ and ends at $1991$. The next starts at $2002$ and ends at $2992$ and so on. It turns out that $110$ radians is $\frac {110}{2\pi} \approx 17.507$ full turns, so an increment of $220$ results in just over $35$ full turns and the next angle is $0.014$ full turns or $5^\circ$. That explains the groups of $5$. The jump to the next group is $1001$, which represents about $159.314$ full turns. It will be about $1/3$ of a turn around from the previous group.

enter image description here

Ross Millikan
  • 374,822
0

Here is Mathematica code for anyone else interested in playing around with this (up to $d$ digits in base-$b$):

b=10;d=4;
palindromes = Select[Tuples[Range[b]-1,d],#==Reverse[#]&];
ListPlot[# Cos[#],Sin[#]}&/@FromDigits/@palindromes,AspectRatio->1]]
Semiclassical
  • 15,842