Raíz primitiva
Definición
En aritmética modular, un número se llama raíz primitiva módulo n si todo número coprimo con es congruente con una potencia de módulo . Matemáticamente, es una raíz primitiva módulo n si y solo si para cualquier entero tal que , existe un entero tal que:
.
se llama entonces el índice o logaritmo discreto de en base módulo . también se llama el generador del grupo multiplicativo de enteros módulo .
En particular, para el caso en que es primo, las potencias de la raíz primitiva recorren todos los números de a .
Existencia
Existe raíz primitiva módulo si y solo si:
- es 1, 2, 4, o
- es potencia de un primo impar , o
- es el doble de una potencia de un primo impar .
Este teorema fue demostrado por Gauss en 1801.
Relación con la función de Euler
Sea una raíz primitiva módulo . Entonces podemos mostrar que el menor número para el cual es igual a . Además, el recíproco también es cierto, y este hecho se usará en este artículo para hallar una raíz primitiva.
Más aún, la cantidad de raíces primitivas módulo , si hay alguna, es igual a .
Algoritmo para hallar una raíz primitiva
Un algoritmo naive es considerar todos los números en el rango . Y luego comprobar si cada uno es una raíz primitiva, calculando todas sus potencias para ver si son todas distintas. Este algoritmo tiene complejidad , que sería demasiado lento. En esta sección proponemos un algoritmo más rápido usando varios teoremas conocidos.
De la sección anterior, sabemos que si el menor número para el cual es , entonces es una raíz primitiva. Como para cualquier número coprimo con , sabemos por el teorema de Euler que , entonces para comprobar si es raíz primitiva, basta con comprobar que para todo menor que , . Sin embargo, este algoritmo sigue siendo demasiado lento.
Por el teorema de Lagrange, sabemos que el índice de 1 de cualquier número módulo debe ser un divisor de . Así, basta verificar para todo divisor propio que . Este ya es un algoritmo mucho más rápido, pero todavía podemos hacerlo mejor.
Factoricemos . Demostramos que en el algoritmo anterior basta considerar solo los valores de que tienen la forma . En efecto, sea cualquier divisor propio de . Entonces, obviamente, existe un tal que , es decir . Sin embargo, si , obtendríamos:
.
es decir, entre los números de la forma , habría al menos uno tal que las condiciones no se cumplieran.
Ahora tenemos un algoritmo completo para hallar la raíz primitiva:
-
Primero, hallar y factorizarlo.
-
Luego iterar por todos los números , y para cada número, para comprobar si es raíz primitiva, hacemos lo siguiente:
- Calcular todos los .
- Si todos los valores calculados son distintos de , entonces es una raíz primitiva.
El tiempo de ejecución de este algoritmo es (suponiendo que tiene divisores).
Shoup (1990, 1992) demostró, asumiendo la hipótesis de Riemann generalizada , que es .
Implementación
El siguiente código asume que el módulo p es un número primo. Para que funcione para cualquier valor de p, hay que añadir el cálculo de .
int powmod (int a, int b, int p) {
int res = 1;
while (b)
if (b & 1)
res = int (res * 1ll * a % p), --b;
else
a = int (a * 1ll * a % p), b >>= 1;
return res;
}
int generator (int p) {
vector<int> fact;
int phi = p-1, n = phi;
for (int i=2; i*i<=n; ++i)
if (n % i == 0) {
fact.push_back (i);
while (n % i == 0)
n /= i;
}
if (n > 1)
fact.push_back (n);
for (int res=2; res<=p; ++res) {
bool ok = true;
for (size_t i=0; i<fact.size() && ok; ++i)
ok &= powmod (res, phi / fact[i], p) != 1;
if (ok) return res;
}
return -1;
}