So I could have kept calculating the totient function directly and left it to run for years. Instead, I googled totient and discovered an interesting little mathematical quirk that would enable me to significantly reduce my testing set.
If a number is a product of two prime numbers …
- n = p1 * p2
then the totient of that number is …
- phi (n) = (p1-1) * (p2-1)
The square root of 10E7 is about 3100 plus change. So I calculated all the prime numbers 30% above and below that number. Then tested each of those numbers whereby the products were < 10E7. Smallest n/phi(n) wins. Routine runs in about 5 minutes. By the way, I wrote this routine to leverage (and re-learn Pythonian OOP).
#Euler70 from math import sqrt class Algorithms: def permutate(self, sequence): if not sequence: return [sequence] else: set = [] for k in range(len(sequence)): part = sequence[:k] + sequence[k+1:] for m in self.permutate(part): set.append(sequence[k:k+1] + m) return set def primeGen (self, lowerLimit, upperLimit): isPrime = [False, True] i = 2 while i < upperLimit: isPrime.append(True) i += 1 p = 2 while p * p < upperLimit: if isPrime[p] == True: m = p * p while m < upperLimit: if m % p == 0: isPrime[m] = False m += p p += 1 P = [] count = 0 for p in isPrime: if p and count >= lowerLimit : P.append(count) count += 1 return P class Problem70: def __init__(self, limit): self.limit = limit self.Euler = Algorithms() def Solution(self): primes = self.Euler.primeGen(.7 * sqrt(self.limit), 1.3 * sqrt(self.limit)) print "Primes:"+str(primes)+"\n" i = 0 minNDivPhi = 10 minN = 0 for p1 in primes: print "Testing:"+str(p1) i += 1 for p2 in primes[i:]: n = p1 * p2 if n > self.limit: break phi = (p1-1) * (p2-1) nDivPhi = n / float(phi) if nDivPhi < minNDivPhi: if str(int(n)) in self.Euler.permutate (str(int(phi))): minNDivPhi = nDivPhi minN = n print "n:"+str(n)+" phi:"+str(phi)+" nDivPhi:"+str(nDivPhi) return "Value of n where nDivPhi is minimized:"+str(minN) if __name__ == '__main__': P = Problem70(10000000) print P.Solution()