Search code examples
pythonmathbignum

Is there a fast algorithm for Euler's totient function of BIG numbers (128bit)?


I need to get phi-function (Euler's) of some randomly generated 128-bit numbers. I tried to use this code below but computer is just thinking too much.

import fractions

def phi(n):
    amount = 0        
    for k in range(1, n + 1):
        if fractions.gcd(n, k) == 1:
            amount += 1
    return amount

Is there something faster?


Solution

  • For 128-bit numbers, you're going to want to efficiently compute the prime factorization of n, then use

    totient = n
    for factor in prime_factors(n):
        totient -= totient // factor
    

    The hard part is the factorization. For 128-bit numbers, simple trial division is going to be horribly inefficient. Something like elliptic curve factorization or a quadratic sieve would be better, but writing those by hand is hard. It's probably better to use a library.

    The best Python implementation of a large-number factorization algorithm I've found is, believe it or not, this answer by primo on codegolf.stackexchange.com. It's a multiple-polynomial quadratic sieve.

    primefac (Python 2) and labmath (Python 3) incorporate a quadratic sieve, but it's based on an old, somewhat slow and buggy version of the Code Golf answer. If you want the fixed version, you'll want to go to the Code Golf answer. (Also, be aware that labmath.factorint defaults to not using the mpqs implementation.) labmath and primefac also include elliptic curve factorization, and a few other algorithms that are less likely to be useful for this input size.

    Aside from that, there's sympy.ntheory.factorint, but it had problems with large factors in my tests, and it only has trial division, pollard rho, and pollard p-1 factorization.


    Anyway, use one of those existing factorization options, or implement your own, or whatever, and then build your totient function on top of that. For example, using primefac:

    # primefac.factorint(n) returns a factor:multiplicity dict
    from primefac import factorint
    
    def totient(n):
        totient = n
        for factor in factorint(n):
            totient -= totient // factor
        return totient