## Question

By replacing the 1st digit of *3, it turns out that six of the nine possible values: 13, 23, 43, 53, 73, and 83, are all prime.

By replacing the 3rd and 4th digits of 56**3 with the same digit, this 5-digit number is the first example having seven primes among the ten generated numbers, yielding the family: 56003, 56113, 56333, 56443, 56663, 56773, and 56993. Consequently 56003, being the first member of this family, is the smallest prime with this property.

Find the smallest prime which, by replacing part of the number (not necessarily adjacent digits) with the same digit, is part of an eight prime value family.

import Data.List (sort, group)

primes :: [Int]
primes = 2 : sieve primes [3,5..] where
sieve (p:ps) xs = h ++ sieve ps [x | x <- t, rem x p /= 0]
where (h, t) = span (< p*p) xs

isPrime :: Int -> Bool
isPrime n | n < 1 = False
| otherwise = not $or [n rem x == 0 | x <- [2..floor$ sqrt $fromIntegral n]] groupFrequency :: Ord a => [a] -> [(Int, a)] groupFrequency xs = reverse$ sort [(length cs, head cs) | cs <- group $sort xs] candidates :: [(Char, String)] candidates = [(snd$ head g, s) | p <- primes,
let s = show p,
let g = groupFrequency s,
any ((== 3) . fst) g]

replace :: Eq a => a -> a -> [a] -> [a]
replace old new = foldr (\x acc -> (if x == old then new else x) : acc) []

expand :: (Char, String) -> [String]
expand (d, ds) = [new | n <- ['1'..'9'], let new = replace d n ds, isPrime (read new)]

main :: IO ()
main = putStrLn $snd . head$ filter ((== 8) . length . expand) candidates
$ghc -O2 -o prime-replacement prime-replacement.hs$ time ./prime-replacement
real   0m0.131s
user   0m0.124s
sys    0m0.000s

## Python

#!/usr/bin/env python
from itertools import *
def primes(n):
if n==2: return [2]
elif n<2: return []
s=list(range(3,n+1,2))
mroot = n ** 0.5
half=(n+1)//2-1
i=0
m=3
while m <= mroot:
if s[i]:
j=(m*m-3)//2
s[j]=0
while j<half:
s[j]=0
j+=m
i=i+1
m=2*i+3
return [2]+[x for x in s if x]

from bisect import bisect_left
# sqrt(1000000000) = 31622
__primes = primes(31622)
def is_prime(n):
# if prime is already in the list, just pick it
if n <= 31622:
i = bisect_left(__primes, n)
return i != len(__primes) and __primes[i] == n
# Divide by each known prime
limit = int(n ** .5)
for p in __primes:
if p > limit: return True
if n % p == 0: return False
# fall back on trial division if n > 1 billion
for f in range(31627, limit, 6): # 31627 is the next prime
if n % f == 0 or n % (f + 4) == 0:
return False
return True

def number_families(num):
digits = [d for d in str(num)]
products = list(product((True, False), repeat=len(digits)))[1:-1]
for p in products:
pattern = ''
for i, x in enumerate(p):
if x:
pattern += digits[i]
else:
pattern += 'X'
yield [int(pattern.replace('X', str(n))) for n in range(10)]

def main():
for prime in primes(1000000):
for number_family in number_families(prime):
prime_family = [n for n in number_family if is_prime(n) and len(str(n)) == len(str(prime))]
if len(prime_family) == 8 and prime in prime_family:
print(prime)
return

if __name__ == "__main__":
main()
\$ time python3 changing-primes.py
real   0m13.523s
user   0m13.416s
sys    0m0.020s