Función φ de Euler
La función φ de Euler, también conocida como función phi , cuenta la cantidad de enteros entre 1 y inclusive que son coprimos con . Dos números son coprimos si su máximo común divisor es igual a (se considera que es coprimo con cualquier número).
Estos son los valores de para los primeros enteros positivos:
Propiedades
Las siguientes propiedades de la función φ de Euler bastan para calcularla para cualquier número:
- Si es un número primo, entonces para todo . Por lo tanto tenemos:
- Si es un número primo y , entonces hay exactamente números entre y que son divisibles por . Lo que nos da:
-
Si y son coprimos, entonces:
Esta relación no es trivial de ver. Se sigue del Teorema Chino del Resto. El Teorema Chino del Resto garantiza que, para cada y cada , existe un único con y . No es difícil mostrar que es coprimo con si y solo si es coprimo con e es coprimo con . Por lo tanto, la cantidad de enteros coprimos con es igual al producto de las cantidades correspondientes a y a .
-
En general, cuando y no son coprimos, la ecuación
con se cumple.
Así, usando las primeras tres propiedades, podemos calcular a través de la factorización de (descomposición de en un producto de sus factores primos). Si , donde son factores primos de ,
Implementación
Esta es una implementación que usa factorización en :
int phi(int n) {
int result = n;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
while (n % i == 0)
n /= i;
result -= result / i;
}
}
if (n > 1)
result -= result / n;
return result;
}Función φ de Euler de a en { #etf_1_to_n data-toc-label=“Función φ de Euler de 1 a n en <script type=“math/tex”>O(n log log n)” }
Si necesitamos de todos los números entre y , entonces factorizar los números no es eficiente. Podemos usar la misma idea que la Criba de Eratóstenes. Sigue basándose en la propiedad mostrada más arriba, pero en lugar de actualizar el resultado temporal para cada factor primo de cada número, encontramos todos los números primos y, para cada uno, actualizamos los resultados temporales de todos los números que son divisibles por ese número primo.
Como este enfoque es básicamente idéntico a la Criba de Eratóstenes, la complejidad también será la misma:
void phi_1_to_n(int n) {
vector<int> phi(n + 1);
for (int i = 0; i <= n; i++)
phi[i] = i;
for (int i = 2; i <= n; i++) {
if (phi[i] == i) {
for (int j = i; j <= n; j += i)
phi[j] -= phi[j] / i;
}
}
}Calcular φ de a usando la criba segmentada { data-toc-label=“Calcular φ de L a R usando la criba segmentada” }
Si necesitamos de todos los números entre y , podemos usar el enfoque de la criba segmentada.
El algoritmo primero precalcula todos los primos hasta usando una criba lineal en tiempo y espacio . Para cada número del rango , aplica después la fórmula de basada en la factorización iterando sobre estos primos. Mantenemos un arreglo de restos para rastrear la parte no factorizada de cada número. Si un resto sigue siendo mayor que 1 después de procesar todos los primos chicos, indica un factor primo grande mayor que , que se trata en una pasada final. La complejidad total del cálculo sobre el rango es .
const long long MAX_RANGE = 1e6 + 6;
vector<long long> primes;
long long phi[MAX_RANGE], rem[MAX_RANGE];
vector<int> linear_sieve(int n) {
vector<bool> composite(n + 1, 0);
vector<int> prime;
// 0 y 1 no son compuestos (ni primos)
composite[0] = composite[1] = 1;
for(int i = 2; i <= n; i++) {
if(!composite[i]) prime.push_back(i);
for(int j = 0; j < prime.size() && i * prime[j] <= n; j++) {
composite[i * prime[j]] = true;
if(i % prime[j] == 0) break;
}
}
return prime;
}
// Para obtener el valor de phi(x) con L <= x <= R, usar phi[x - L].
void segmented_phi(long long L, long long R) {
for(long long i = L; i <= R; i++) {
rem[i - L] = i;
phi[i - L] = i;
}
for(long long i : primes) {
for(long long j = max(i * i, (L + i - 1) / i * i); j <= R; j += i) {
phi[j - L] -= phi[j - L] / i;
while(rem[j - L] % i == 0) rem[j - L] /= i;
}
}
for(long long i = 0; i < R - L + 1; i++) {
if(rem[i] > 1) phi[i] -= phi[i] / rem[i];
}
}Propiedad de la suma de divisores { #divsum}
Esta propiedad interesante la estableció Gauss:
Aquí la suma recorre todos los divisores positivos de .
Por ejemplo, los divisores de 10 son 1, 2, 5 y 10. De ahí .
Calcular φ de 1 a usando la propiedad de la suma de divisores { data-toc-label=“Calcular φ de 1 a n usando la propiedad de la suma de divisores” }
La propiedad de la suma de divisores también nos permite calcular de todos los números entre 1 y . Esta implementación es un poco más simple que la implementación anterior basada en la Criba de Eratóstenes; sin embargo, también tiene una complejidad un poco peor:
void phi_1_to_n(int n) {
vector<int> phi(n + 1);
phi[0] = 0;
phi[1] = 1;
for (int i = 2; i <= n; i++)
phi[i] = i - 1;
for (int i = 2; i <= n; i++)
for (int j = 2 * i; j <= n; j += i)
phi[j] -= phi[i];
}Aplicación en el teorema de Euler { #application }
La propiedad más famosa e importante de la función φ de Euler se expresa en el teorema de Euler:
En el caso particular en que es primo, el teorema de Euler se convierte en el pequeño teorema de Fermat:
El teorema de Euler y la función φ de Euler aparecen con bastante frecuencia en aplicaciones prácticas; por ejemplo, ambos se usan para calcular el inverso multiplicativo modular.
Como consecuencia inmediata también obtenemos la equivalencia:
Esto permite calcular para muy grandes, especialmente si es el resultado de otro cálculo, ya que permite calcular bajo un módulo.
Teoría de grupos
es el orden del grupo multiplicativo módulo n , es decir, el grupo de unidades (elementos con inversos multiplicativos). Los elementos con inversos multiplicativos son precisamente los coprimos con .
El orden multiplicativo de un elemento módulo , denotado , es el menor tal que . es el tamaño del subgrupo generado por , así que, por el teorema de Lagrange, el orden multiplicativo de cualquier debe dividir a . Si el orden multiplicativo de es , el mayor posible, entonces es una raíz primitiva y el grupo es cíclico por definición.
Generalización
Hay una versión menos conocida de la última equivalencia, que permite calcular de forma eficiente cuando y no son coprimos. Para arbitrarios y :
Demostración:
Sean los divisores primos comunes de y , y sus exponentes en . Con ellos definimos , lo que hace que sea coprimo con . Y sea el menor número tal que divide a . Asumiendo , podemos escribir:
La equivalencia entre la tercera y la cuarta línea se sigue del hecho de que . En efecto, si con , entonces con .
Como y son coprimos, podemos aplicar el teorema de Euler y obtener la fórmula eficiente (ya que es muy chico; de hecho ):
Esta fórmula es difícil de aplicar, pero podemos usarla para analizar el comportamiento de . Podemos ver que la sucesión de potencias entra en un ciclo de longitud después de los primeros (o menos) elementos. divide a (porque y son coprimos tenemos ), por lo tanto también podemos decir que el período tiene longitud . Y como , podemos concluir la fórmula deseada, mucho más simple:
Problemas de práctica
- SPOJ #4141 “Euler Totient Function” [Difficulty: CakeWalk]
- UVA #10179 “Irreducible Basic Fractions” [Difficulty: Easy]
- UVA #10299 “Relatives” [Difficulty: Easy]
- UVA #11327 “Enumerating Rational Numbers” [Difficulty: Medium]
- TIMUS #1673 “Admission to Exam” [Difficulty: High]
- UVA 10990 - Another New Function
- Codechef - Golu and Sweetness
- SPOJ - LCM Sum
- GYM - Simple Calculations (F)
- UVA 13132 - Laser Mirrors
- SPOJ - GCDEX
- UVA 12995 - Farey Sequence
- SPOJ - Totient in Permutation (easy)
- LOJ - Mathematically Hard
- SPOJ - Totient Extreme
- SPOJ - Playing with GCD
- SPOJ - G Force
- SPOJ - Smallest Inverse Euler Totient Function
- Codeforces - Power Tower
- Kattis - Exponial
- LeetCode - 372. Super Pow
- Codeforces - The Holmes Children
- Codeforces - Small GCD