【问题标题】:Computing Eulers Totient Function计算欧拉总函数
【发布时间】:2013-08-07 21:27:33
【问题描述】:

我正在尝试找到一种有效的方法来计算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)

【问题讨论】:

  • 1/i 并没有像您认为的那样做 - 试试吧。
  • 使用 python3 代替 python2 :-)
  • 或将from __future__ import division 放在代码顶部以在 Python 2 中启用浮点除法。
  • 欧拉计划吧?我会事先编译一个素数列表,或者至少缓存你找到的素数。

标签: python


【解决方案1】:

根据 Wikipedia 上的描述,这是一种更快、更有效的方法:

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

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

from math import gcd

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

【讨论】:

  • 你的意思是 range(1, n+1) 吗?
  • 这些属性很方便如果 p 是素数, φ(p)=p−1 as gcd(p,q)=1其中 10, ϕ(p^k)=p^k−p^(k−1) 如果a和b互质,则ϕ(ab)=ϕ(a).ϕ(b) e-maxx-eng.github.io/algebra/phi-function.html
【解决方案2】:

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

  1. y 需要等于 n 作为初始值,而不是 1
  2. 正如一些在 cmets 中提到的,不要使用整数除法
  3. n % i == 0 is True 没有按照你的想法做,因为 Python 链接了比较!即使n % i 等于0 然后0 == 0True 但是 0 is TrueFalse!使用括号或直接与 True 进行比较,因为这不是必需的。

解决这些问题,

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)

【讨论】:

    【解决方案3】:

    为范围内的每一对计算 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
    

    【讨论】:

    • 这可以通过重新设计循环来加快速度,使其停止在当前 n 值的平方根处,而不是初始 n 值。
    • 不过,它至少胜过所有其他现有答案使用的显式素性测试和 gcd 计算。
    【解决方案4】:

    我正在使用 python 开发一个加密库,这就是我正在使用的。 gcd()是欧几里得计算最大公约数的方法,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
    

    【讨论】:

      【解决方案5】:

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

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

      好的,现在我们可以用数组中的搜索替换 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 整除,剩下 12 个与 36 互质的数。确实有 12 个与 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
      

      【讨论】:

        【解决方案6】:

        看起来您正在尝试使用欧拉的乘积公式,但您没有计算除以 a 的素数数。您正在计算与 a 互质的元素的数量。

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

          • 您没有考虑计算将n2*k 进行比较所需的工作以及减去n-k 所需的工作。
          【解决方案8】:

          这里是 orlp 答案的一个较短的实现。

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

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

          【讨论】:

            【解决方案9】:

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

            因此,您的代码中几乎没有错误:
            1.y应该等于n
            2.1/i实际上1i都是整数,所以它们的求值也是整数,会导致错误的结果。

            这是需要更正的代码。

            定义 phi(n): y = n 对于范围内的 i (2,n+1): 如果 isPrime(i) 和 n % i == 0 : y -= y/i 别的: 继续 返回整数(y)

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-08-13
              • 2013-09-17
              • 2021-04-17
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多