Project Euler Problem #51

Problem #51 is one of the many problems that involves manipulating digits in prime numbers. The question reads:

Project Euler Problem 51: Prime digit replacements
By replacing the 1st digit of the 2-digit number *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.

My solution for this problem definitely falls into the category of brute force. Here’s my solution:

Solution #1: Brute Force Approach

First, we can notice that the 10 different values which are formed by replacing sections of a prime number with the digits 0-9 should form an arithmetic sequence. If the common difference of this arithmetic sequence is not a multiple of 3, then we would expect at least 3 of the values to be multiples of 3, and thus, the family could have at most 7 primes. Thus, for a 8 prime value family to be found, the common difference should be a multiple of 3. Because the common difference for an arithmetic sequence created in this manner is the sum of powers of 10 representing which digits are being replaced, the common difference can only be a multiple of 3 if the number of digits that is being replaced is a multiple of 3. Thus, it suffices to investigate changing 3 of the digits in primes.

From this point, I proceed by brute force. I iterate through all of the prime numbers that could be the minimum number in the prime family. Then I create arrays representing the positions of the ‘0’, ‘1’, and ‘2’ digits in the prime. Then I list all subsets of each of these arrays with exactly 3 elements. For each subset, I list the possible numbers that can be found by increasing the respective digits in the positions in the subset. Finally, I count the number of primes in the set and check if it is greater than or equal to 8. Once a prime is found which generates at least 8 primes, the problem is solved. Here is an implementation of this long approach in Python 2.7:

 '''
 Author: Walker Kroubalkian
 Brute Force Approach to Project Euler Problem #51
 '''
 
 import time
 from math import sqrt
 
 def listSubsets(myList,n):
     l = len(myList)
     if(l<n):
         return []
     if(n==0):
         return [[]]
     a = myList[0]
     first = listSubsets(myList[1:],n)
     second = listSubsets(myList[1:],n-1)
     total = first[:]
     for b in second:
         c = b[:]
         c.append(a)
         total.append(sorted(c))
     return total
 
 def primePossibilities(x):
     a = str(x)
     l = len(a)
     possibilities = []
     twos = []
     ones = []
     zeros = []
     for y in range(l):
         v = int(a[y])
         if(v==2):
             twos.append(y)
         elif(v==1):
             ones.append(y)
         elif(v==0):
             zeros.append(y)
     zeroTriples = listSubsets(zeros, 3)
     oneTriples = listSubsets(ones, 3)
     twoTriples = listSubsets(twos, 3)
     for triple in zeroTriples:
         firstIndex = triple[0]
         secondIndex = triple[1]
         thirdIndex = triple[2]
         total = [x]
         for c in range(1,10):
             letter = str(c)
             total.append(int(a[0:firstIndex] + letter+ a[firstIndex+1:secondIndex]+letter + a[secondIndex+1:thirdIndex]+letter+a[thirdIndex+1:]))
         possibilities.append(total)
     for triple in oneTriples:
         firstIndex = triple[0]
         secondIndex = triple[1]
         thirdIndex = triple[2]
         total = [x]
         for c in range(2,10):
             letter = str(c)
             total.append(int(a[0:firstIndex] + letter+ a[firstIndex+1:secondIndex]+letter + a[secondIndex+1:thirdIndex]+letter+a[thirdIndex+1:]))
         possibilities.append(total)
     for triple in twoTriples:
         firstIndex = triple[0]
         secondIndex = triple[1]
         thirdIndex = triple[2]
         total = [x]
         for c in range(3,10):
             letter = str(c)
             total.append(int(a[0:firstIndex] + letter+ a[firstIndex+1:secondIndex]+letter + a[secondIndex+1:thirdIndex]+letter+a[thirdIndex+1:]))
         possibilities.append(total)
     return possibilities
 
 def isPrime(n):
     if(n<=1):
         return False
     if(n<4):
         return True
     if(n%2==0):
         return False
     c=3
     while(c<=sqrt(n)):
         if(n%c==0):
             return False
         c+=2
     return True
 
 def projectEulerProblemFiftyOne(n):
     found = 0
     possible = 3
     current = -1
     while(found<n):
         if(isPrime(possible)):
             a = primePossibilities(possible)
             for replaced in a:
                 total = 0
                 for x in replaced:
                     if isPrime(x):
                         total+=1
                 if(total>=8):
                     current = possible
                     found+=1
         possible+=2
     return current
 
 start = time.time()
 print projectEulerProblemFiftyOne(1)
 print ("--- %s seconds ---" % (time.time()-start))
 
 '''
 Prints
 
 121313
 --- 0.314001083374 seconds ---
 
 for input of n=1
 ''' 

And with that, we’re done. The solution surprisingly ran in only a third of a second. I expected the answer to be at least a few orders of magnitude higher. I may come back to this question eventually to look for a more efficient approach.

Thanks for reading! See you tomorrow.

Published by Walker Kroubalkian

My name is Walker Kroubalkian. I really enjoy math, computer science, and hiking.

Leave a comment

Design a site like this with WordPress.com
Get started