我正在尝试找到一种有效的方法来计算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)
答案 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)
你有三个不同的问题......
y
需要等于n
作为初始值,而不是1
n % i == 0 is True
没有按照你的想法行事!即使n % i
等于0
,0 == 0
仍为True
但 0 is True
为False
!使用parens或只是摆脱与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)
答案 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
实际上1
和i
两者都是整数,因此它们的评估也将是整数,因此会导致错误的结果。
以下是需要更正的代码。
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)])
正如其他人已经提到的那样,它为性能优化留有空间。