其他用户提到的大多数实现都依赖于调用 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 整除,剩下 12 个与 36 互质的数。确实有 12 个正整数与 36 互质且小于 36:1、5、7、11、13、17、19、23 、25、29、31 和 35。
TL;博士
换句话说:我们必须找到我们数字的所有质因数,然后使用 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