Question
Starting with 1 and spiralling anticlockwise in the following way, a square spiral with side length 7 is formed.
37 36 35 34 33 32 31
38 17 16 15 14 13 30
39 18 5 4 3 12 29
40 19 6 1 2 11 28
41 20 7 8 9 10 27
42 21 22 23 24 25 26
43 44 45 46 47 48 49
It is interesting to note that the odd squares lie along the bottom right diagonal, but what is more interesting is that 8 out of the 13 numbers lying along both diagonals are prime; that is, a ratio of \frac{8}{13} \approx 62\%.
If one complete new layer is wrapped around the spiral above, a square spiral with side length 9 will be formed. If this process is continued, what is the side length of the square spiral for which the ratio of primes along both diagonals first falls below 10%?
Haskell
isPrime :: Int -> Bool
| n < 1 = False
isPrime n | otherwise = not $ or [n `rem` x == 0 | x <- [2..floor $ sqrt $ fromIntegral n]]
primeCorners :: Int -> Int
= sum [1 | x <- [1..3], isPrime $ n^2 - x*(n - 1)]
primeCorners n
expansion :: [(Int, Int)]
= scanl (\(p, t) x -> (p + primeCorners x, t + 4)) (0, 1) [3,5..]
expansion
main :: IO ()
= print $ 3 + 2 * (length $ takeWhile id $ tail [10*p >= t | (p, t) <- expansion]) main
$ ghc -O2 -o spiral-primes spiral-primes.hs
$ time ./spiral-primes
real 0m0.714s
user 0m0.714s
sys 0m0.000s
Python
#!/usr/bin/env python
import math
from collections import defaultdict
from functools import reduce
def factorize(n):
if n < 1:
raise ValueError('fact() argument should be >= 1')
if n == 1:
return [] # special case
= []
res # iterate over all even numbers first.
while n % 2 == 0:
2)
res.append(//= 2
n # try odd numbers up to sqrt(n)
= math.sqrt(n+1)
limit = 3
i while i <= limit:
if n % i == 0:
res.append(i)//= i
n = math.sqrt(n+i)
limit else:
+= 2
i if n != 1:
res.append(n)return res
def num_divisors(n):
= sorted(factorize(n))
factors = defaultdict(int)
histogram for factor in factors:
+= 1
histogram[factor] # number of divisors is equal to product of
# incremented exponents of prime factors
from operator import mul
try:
return reduce(mul, [exponent + 1 for exponent in list(histogram.values())])
except:
return 1
def is_prime(num):
if num % 2 == 0:
return False
if num % 3 == 0 and num != 3:
return False
if num_divisors(num) == 2 and num > 1:
return True
else:
return False
def spiral_diagonals():
= 1
n = 2
step = 0
since_last while True:
yield n
+= step
n += 1
since_last if since_last == 4:
+= 2
step = 0
since_last
def main():
= 0
level = 0
primes for i, n in enumerate(spiral_diagonals()):
if (i-1) % 4 == 0:
+= 1
level
if is_prime(n):
+= 1
primes = (2 * level) + 1
side_length
= float(primes) / float(i+1)
ratio if 0 < ratio < 0.1:
print(side_length)
return
if __name__ == "__main__":
main()
$ time python3 spiral-primes.py
real 0m10.777s
user 0m10.776s
sys 0m0.000s