0

I was solving Euler's project question number 70 and my Euler's totient function was slow. Can anyone help?

Euler project Question 70 description:

Euler's Totient function, φ(n) [sometimes called the phi function], is used to determine the number of positive numbers less than or equal to n which are relatively prime to n. For example, as 1, 2, 4, 5, 7, and 8, are all less than nine and relatively prime to nine, φ(9)=6. The number 1 is considered to be relatively prime to every positive number, so φ(1)=1.

Interestingly, φ(87109)=79180, and it can be seen that 87109 is a permutation of 79180.

Find the value of n, 1 < n < N, for which φ(n) is a permutation of n and the ratio n/φ(n) produces a minimum.

Input format: Contains and integer N

Constraints: 1<=N<=10**7

Output format: Print the answer corresponding to the test case

Sample Input: 100

Sample Output: 21

This optimized code does not pass for 5 cases out of 10. The phi function is slow. I don't know what else to do to optimize it.

from math import gcd
from itertools import permutations

def totatives(n):
    phi = int(n > 1 and n)
    for p in range(2, int(n ** .5) + 1):
        if not n % p:
            phi -= phi // p
            while not n % p:
                n //= p
    #if n is > 1 it means it is prime
    if n > 1: phi -= phi // n 
    return phi

def permute(num,phi_num):
    temp="".join(sorted(str(num)))
    phi_num="".join(sorted(str(phi_num)))
    return temp==phi_num

N=int(input())
d={}
for n in range(12,N):
    if permute(n,totatives(n)):
        #print(permute,phi(n))
        d[n]=(n/totatives(n))
    
#print(d)
min_b=min(d.values())
for a,b in d.items():
    if b==min_b:
        print(a)
        break

  • If your code doesn't work, Stackoverflow [might be the right place to ask](/help/on-topic), but if your code _works_ and you just want help with optimizing it, then Stackoverflow is is not the right place to post. Hit up https://codereview.stackexchange.com instead. – Mike 'Pomax' Kamermans Aug 05 '22 at 23:13
  • 1
    HINT: Picking each number and figuring out its φ takes work. Finding φ for all numbers in a range can be done much more easily. – btilly Aug 06 '22 at 01:40

1 Answers1

0

You should propagate primes to compute phi for their multiples in a list of all values. 10**7 values is manageable in a list and you can use the sieve of Eratosthenes to quickly run through primes.

def totients(N):
    result  = list(range(N+1))  # result[n] will contain totient of n
    isPrime = [True]*(N+1)      # sieve of Eratosthenes
    for p in range(2,N+1):
        if not isPrime[p]: continue # only propagate primes factors
        for i in range(p,N+1,p):
            result[i] -= result[i]//p # progressively compute phi for multiples
            isPrime[i] = False        # flag prime factors (sieve)
    return result

def isPermuted(A,B): return sorted(str(A))==sorted(str(B))

Output:

N = 100
permutedTots = [ (n,t) for n,t in enumerate(totients(N)) 
                 if n>1 and isPermuted(n,t) ]

print(permutedTots)
# [(21, 12), (63, 36)]

minPermuted  = min(permutedTots, key=lambda nt:nt[0]/nt[1])
print(minPermuted)
# (21, 12)

This isn't extremely fast though. It takes 22 seconds to compute totients(10**7)

Alain T.
  • 40,517
  • 4
  • 31
  • 51