计算欧拉的商函数

时间:2013-08-07 21:27:33

标签: python

我正在尝试找到一种有效的方法来计算Euler's totient function

这段代码有什么问题?它似乎没有用。

def isPrime(a):
    return not ( a < 2 or any(a % i == 0 for i in range(2, int(a ** 0.5) + 1)))

def phi(n):
    y = 1
    for i in range(2,n+1):
        if isPrime(i) is True and n % i  == 0 is True:
            y = y * (1 - 1/i)
        else:
            continue
    return int(y)

9 个答案:

答案 0 :(得分:23)

基于维基百科上的描述,这是一种更快,更有效的方式:

  

因此,如果n是正整数,则φ(n)是gcd(n,k)= 1的范围1≤k≤n的整数k。

我不是说这是最快或最干净的,但它确实有效。

import fractions

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

答案 1 :(得分:5)

你有三个不同的问题......

  1. y需要等于n作为初始值,而不是1
  2. 正如有些人在评论中提到的那样,不要使用整数除法
  3. 由于Python链接比较,
  4. n % i == 0 is True没有按照你的想法行事!即使n % i等于00 == 0仍为True 0 is TrueFalse!使用parens或只是摆脱与True的比较,因为无论如何都不需要。
  5. 解决这些问题,

    def phi(n):
        y = n
        for i in range(2,n+1):
            if isPrime(i) and n % i == 0:
                y *= 1 - 1.0/i
        return int(y)
    

答案 2 :(得分:3)

我正在使用python中的加密库,这就是我正在使用的。 gcd()是Euclid计算最大公约数的方法,phi()是整数函数。

def gcd(a, b):
    while b:
        a, b=b, a%b
    return a
def phi(a):
    b=a-1
    c=0
    while b:
        if not gcd(a,b)-1:
            c+=1
        b-=1
    return c

答案 3 :(得分:2)

看起来你正在尝试使用Euler的产品公式,但是你没有计算除以a的素数的数量。你正在计算相对素数的元素数量。

另外,由于1和i都是整数,所以除法也是如此,在这种情况下你总是得到0。

答案 4 :(得分:2)

关于效率,我没有注意到有人提到gcd(k,n)= gcd(n-k,n)。使用这个事实可以节省大约一半涉及使用gcd的方法所需的工作。只需用2开始计数(因为1 / n和(n-1)/ k总是不可简化的)并且每次gcd为1时加2。

答案 5 :(得分:1)

实际上要计算phi(任何数字都说n)
我们使用Formula
其中p是n的主要因素。

因此,您的代码中几乎没有错误:
1。y应该等于n
2.对于1/i实际上1i两者都是整数,因此它们的评估也将是整数,因此会导致错误的结果。

以下是需要更正的代码。

def phi(n):
    y = n
    for i in range(2,n+1):
        if isPrime(i) and n % i  == 0 :
            y -= y/i
        else:
            continue
    return int(y)

答案 6 :(得分:1)

计算范围内每对的gcd效率不高,无法扩展。您不需要遍历所有范围,如果n不是素数,则可以检查素因数直至其平方根,请参阅https://stackoverflow.com/a/5811176/3393095。 然后,我们必须通过phi = phi *(1-1 / prime)更新每个素数的phi。

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

答案 7 :(得分:1)

其他用户提到的大多数实现都依赖于调用gcd()或isPrime()函数。如果要多次使用phi()函数,则需要事先计算这些值。一种实现方法是使用所谓的筛算法。

https://stackoverflow.com/a/18997575/7217653这个关于stackoverflow的答案为我们提供了一种快速找到所有低于给定数字的素数的方法。

好的,现在我们可以用数组中的搜索替换isPrime()了。

现在有实际的phi函数:

维基百科为我们提供了一个清晰的示例:https://en.wikipedia.org/wiki/Euler%27s_totient_function#Example

  

phi(36)= phi(2 ^ 2 * 3 ^ 2)= 36 *(1- 1/2)*(1- 1/3)= 30 * 1/2 * 2/3 = 12      

换句话说,这表示36的不同素数是2和3;从1到36的36个整数中的一半可被2整除,剩下18个;其中的三分之一可被3整除,剩下十二个与36互质的数。确实有十二个与36互质且小于36的正整数:1、5、7、11、13、17、19、23 ,25、29、31和35。

TL; DR

换句话说:我们必须找到我们所有的素数,然后使用foreach prime_factor将这些素数相乘:n * = 1-1 / prime_factor。

import math

MAX = 10**5

# CREDIT TO https://stackoverflow.com/a/18997575/7217653
def sieve_for_primes_to(n): 
    size = n//2
    sieve = [1]*size
    limit = int(n**0.5)
    for i in range(1,limit):
        if sieve[i]:
            val = 2*i+1
            tmp = ((size-1) - i)//val 
            sieve[i+val::val] = [0]*tmp
    return [2] + [i*2+1 for i, v in enumerate(sieve) if v and i>0]

PRIMES = sieve_for_primes_to(MAX)
print("Primes generated")


def phi(n):
    original_n = n
    prime_factors = []
    prime_index = 0
    while n > 1: # As long as there are more factors to be found
        p = PRIMES[prime_index]
        if (n % p == 0): # is this prime a factor?
            prime_factors.append(p)
            while math.ceil(n / p) == math.floor(n / p): # as long as we can devide our current number by this factor and it gives back a integer remove it
                n = n // p

        prime_index += 1

    for v in prime_factors: # Now we have the prime factors, we do the same calculation as wikipedia
        original_n *= 1 - (1/v)

    return int(original_n)

print(phi(36)) # = phi(2**2 * 3**2) = 36 * (1- 1/2) * (1- 1/3) = 30 * 1/2 * 2/3 = 12

答案 8 :(得分:1)

这是orlp答案的较短实现。

from math import gcd
def phi(n): return sum([gcd(n, k)==1 for k in range(1, n+1)])

正如其他人已经提到的那样,它为性能优化留有空间。