Statutory Warning: Spoilers ahead
This turned out to be really simple, though I went through a phase of churning out complicated solutions because of a stupid mistake.
I didn't preserve intermediate versions, so here is the final solution:
import qualified Data.List as L
import qualified Data.Vector as V
import qualified Data.Function as F
sieve :: Int -> [Int] -> [Int]
sieve num list =
if null list
then [num]
else let rest = filter (\x -> x `mod` num > 0) list
in
num : sieve (head rest) (tail rest)
eratosthenes :: Int -> [Int]
eratosthenes maxNum = sieve 2 [3 .. maxNum]
allPrimes :: Int -> V.Vector Int
allPrimes maxNum = V.fromList $ eratosthenes maxNum
eulerEqn :: Int -> Int -> Int -> Int
eulerEqn a b n = n * n + a * n + b
-- TODO(agam): replace with the "standard" way to do binary search
binSearch :: V.Vector Int -> Int -> Int -> Int -> Bool
binSearch arr min max elem =
let low = arr V.! min
high = arr V.! max
in
if max - min < 2
then if low == elem
then True
else False
else let mid = div (min + max) 2
midElem = arr V.! mid
in
if midElem > elem
then binSearch arr min mid elem
else binSearch arr mid max elem
consecutivePrimes a b primes start length =
let p = eulerEqn a b start
l = V.length primes
isPrime = binSearch primes 0 l p
in
if isPrime
then consecutivePrimes a b primes (start + 1) (length + 1)
else length
numPrimes a b primes = consecutivePrimes a b primes 0 0
euler27 maxNum =
let ap = allPrimes maxNum
primeLengths = [(a * b, numPrimes a b ap) | a <- [-1000 .. 1000], b <- [-1000 .. 1000]]
in
L.maximumBy (F.on compare snd) primeLengths
When I ran this (i.e. euler27 1000
) I got the correct answer the first time! But I didn't see it. Instead, I entered the second value of the tuple, which is not the one asked for, and it was therefore obviously wrong. So I thought "hmm, we're looking at some large repeating sequence among really large primes", and tried euler27 100000
and euler27 10000000
, with no luck.
The last one kept running for hours and I killed it, and then came up with the idea of "vectorizing everything" – which was perhaps a good academic exercise but did absolutely nothing for the performance here.
So I forgot about it for a while, then came back and ran euler27 1000
, entered the first value of the tuple, and that was the end of this story.