---
tags: number-theory, sieve-of-eratosthenes, prime-numbers, sieve, divisors-sieve, lowest-prime, greatest-prime
---
# Criba de Eratóstenes
Este es uno de los algoritmos más clásicos y a la vez eficientes que se relacionan con los números primos. Su objetivo es el siguiente: obtener todos los números primos entre $1$ y $n$.
## Motivación
Una forma sencilla de obtenerlos es reusar el test primalidad que vimos antes para cada número en el rango $[1, n]$, e ir guardando todos los números que resulten primos:
### Algoritmo 1
```cpp=
vector<int> getPrimes(int n){
vector<int> primes;
for(int i = 1; i <= n; i++){
if(isPrime(i)){
primes.push_back(i);
}
}
return primes;
}
```
Si usamos el test de primalidad optimizado, la complejidad es aproximadamente $O(\sqrt{1} + \sqrt{2} + \cdots + \sqrt{n}) = O(n \sqrt{n})$, la cual es aceptable para $n \leq 10^5$. Veámos cómo mejorar esta complejidad.
## Criba básica
La idea principal de la criba de Eratóstenes es tener un arreglo de tamaño $n$ tal que la posición $i$ nos diga si $i$ es primo o no. Al principio marcaremos todos los números como primos, excepto al $1$.
- Comenzando con el $2$, que es el primer primo, iremos tachando todos sus múltiplos mayores en el arreglo ($4, 6, 8, 10, \ldots$), pues sabemos que ninguno de ellos será primo, ya que el $2$ los divide a todos ellos.
- Luego seguimos con el $3$, que es el siguiente primo, y haremos lo mismo, tachar todos sus múltiplos mayores ($6, 9, 12, 15, \ldots$) como números compuestos.
- Al momento de seguir con el $4$ podríamos tachar todos sus múltiplos ($8, 12, 16, \ldots$), pero sorpresa, todos ellos ya fueron tachados por el $2$, entonces no tiene sentido volverlos a tachar.
- Siguiendo con el $5$ vemos que no ha sido tachado por nadie hasta ahora, entonces también tiene que ser primo, y tachamos todos sus múltiplos mayores ($10, 15, 20, 25, \ldots$).
- Seguimos avanzando y cada que encontremos un número sin tachar, marcamos todos sus múltiplos mayores.
Por lo tanto, tenemos la siguiente propiedad: un número $n$ es primo si no es divisible entre los primos $p$ tales que $p < n$. Este es un resultado más fuerte que el que usamos en el test de primalidad básico: verificábamos que $n$ no fuera divisible por ningún número en el rango $[2, n-1]$, ahora podemos verificar únicamente con los primos en ese rango, y esa es la idea sobre la que descansa la correctitud de la criba, ya que si llegamos a la $i$-ésima posición y nadie la ha tachado, es porque no hubo primos menores a $i$ que lo dividieran, por lo tanto $i$ tiene que ser primo.
De esta forma, todos los números que queden sin tachar en el arreglo al final del algoritmo son primos, y todos los tachados son compuestos.
:::info
:information_source: **Ejemplo:** veamos cómo se ven las iteraciones del algoritmo, suponiendo que queremos obtener los primos entre $1$ y $20$:
Al principio nuestro arreglo de marquitas se ve así, todos los números son primos excepto el 1:
$$
\require{cancel}
\begin{array}{|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|}
\hline
\cancel{1} & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15 & 16 & 17 & 18 & 19 & 20 \\
\hline
\end{array}
$$
Vemos que el $2$ es el primer número sin marcar, por lo tanto marcamos todos sus múltiplos mayores:
$$
\require{cancel}
\begin{array}{|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|}
\hline
\cancel{1} & 2 & 3 & \cancel{4} & 5 & \cancel{6} & 7 & \cancel{8} & 9 & \cancel{10} & 11 & \cancel{12} & 13 & \cancel{14} & 15 & \cancel{16} & 17 & \cancel{18} & 19 & \cancel{20} \\
\hline
\end{array}
$$
El $3$ sigue, por lo tanto:
$$
\require{cancel}
\begin{array}{|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|}
\hline
\cancel{1} & 2 & 3 & \cancel{4} & 5 & \cancel{6} & 7 & \cancel{8} & \cancel{9} & \cancel{10} & 11 & \cancel{12} & 13 & \cancel{14} & \cancel{15} & \cancel{16} & 17 & \cancel{18} & 19 & \cancel{20} \\
\hline
\end{array}
$$
Sigue el $4$, pero como está tachado sabemos que no es primo, por lo tanto no vale la pena tachar sus múltiplos mayores.
Sigue el $5$, y como no está marcado, es primo y podemos tachar todos sus múltiplos. Sin embargo, no obtendremos nuevos taches, y podemos deternos hasta aquí. Se puede demostrar también que con ningún otro número obtendremos nuevos taches, entonces podemos parar aquí.
Finalmente, los números sin tachar son: $\{2, 3, 5, 7, 11, 13, 17, 19\}$, que son exactamente los primos menores o iguales a $20$.
:::
Una primera implementación de esta idea queda como:
### Algoritmo 2: criba de Eratóstenes básica
```cpp=
vector<bool> sieve(int n){
vector<bool> is(n+1, true); // arreglo de marcas inicializado en true, es decir, todos los números son "primos" al inicio
is[0] = is[1] = false; // excepto el 0 y el 1
for(int i = 2; i <= n; i++){
if(is[i]){ // si el número actual es primo, marcamos todos sus múltiplos mayores
for(int j = 2*i; j <= n; j += i){ // el primer múltiplo mayor a i es 2*i, y damos saltos de i en i
is[j] = false;
}
}
}
return is;
}
```
La complejidad de esta implementación es $\displaystyle O(n/2+n/3+n/5+\cdots) = O\left( n \sum_{\substack{p \leq n \\ p \text{ primo}}} \frac{1}{p} \right) = O(n \log \log n)$, es decir, una mejora considerable respecto a $O(n \sqrt{n})$, lo que la hace viable para $n \leq 10^7$. Pero ahora, tenemos una complejidad en memoria de $O(n)$.
Sin embargo, podemos optimizarla aún más. En el ejemplo anterior vimos que llegamos a un punto en donde no obtuvimos más marcas, ¿cuál es dicho punto? Supongamos que estamos procesando el primo $i$, entonces todos sus múltiplos mayores son de la forma $k*i$ para $k \geq 2$. Sea $j = k*i$, entonces para que $j$ sea tachado por ++primera vez++ por el primo $i$, necesitamos que $i$ sea el *divisor primo más pequeño* de $j$, es decir, si $(i, k)$ fuera una pareja de divisores de $j$, entonces $i \leq k$, lo que implica que $i^2 \leq j$, o $j \geq i^2$. Por lo tanto, cambiamos el `j = 2*i` de la línea 6 por `j = i*i`, y así reduciremos el número de taches duplicados.
:::info
:information_source: **Ejemplo:**
- Si estamos procesando el primo $2$, todos los números que tacharemos comenzarán en $2^2=4$, es decir, tachamos $4, 6, 8, 10, \ldots$
- Si estamos procesando el primo $3$, comenzamos a tachar desde $3^2=9$, tachando así $9, 12, 15, 18, \ldots$
- Si estamos procesando el primo $5$, comenzamos a tachar desde $5^2=25$, tachando así $25, 30, 35, 40, \ldots$
- Si estamos procesando el primo $7$, comenzamos a tachar desde $7^2=49$, tachando así $49, 56, 63, 70, \ldots$
:::
Pero como $j \leq n$, entonces también se cumple que $i^2 \leq n$, por lo tanto también hay que cambiar la línea 4 la parte de `i <= n` por `i*i <= n`. Es por eso que en el ejemplo a partir del 5 ya no hubo más taches, pues teníamos que $n=20$, entonces $4^2 \leq 16$ pero $5^2 > 20$.
Aún con estos cambios, la complejidad sigue siendo $O(n \log \log n)$, pero con una constante más pequeña, haciéndola viable incluso para $n \leq 10^8$.
Una optimización más que podemos hacer es similar a la que hicimos con los números pares en el test de primalidad: podemos marcar todos los múltiplos mayores de $2$ como compuestos desde el principio, y cada que tachemos números, solo iteramos por sus múltiplos impares. La implementación quedaría así, ya tomando en cuenta todas las optimizaciones:
### Algoritmo 3: criba de Eratóstenes básica optimizada
```cpp=
vector<bool> sieve(int n){
vector<bool> is(n+1, true); // arreglo de marcas inicializado en true, es decir, todos los números son "primos" al inicio
is[0] = is[1] = false; // excepto el 0 y el 1
for(int i = 4; i <= n; i += 2){
is[i] = false; // marcamos todos los múltiplos mayores de 2 como compuestos
}
for(int i = 3; i*i <= n; i += 2){ // comenzamos a iterar por los impares desde el 3
if(is[i]){ // si el número actual es primo, marcamos todos sus múltiplos mayores
for(int j = i*i; j <= n; j += 2*i){ // comenzamos en i*i y solo iteramos por los impares
is[j] = false;
}
}
}
return is;
}
```
## Obtener el arreglo de primos a través de la criba
Hasta ahorita solo vimos cómo obtener el arreglo si nos dice si un número es primo o no, pero no obtuvimos el arreglo de primos como tal. Para obtenerlo hay dos maneras:
- Ya que tenemos el arreglo de marcas, iteramos por todas las posiciones y guardamos en un arreglo todos los números que sí son primos.
### Algoritmo 4: obtener primos a través de la criba
```cpp=
vector<int> getPrimes(int n){
vector<bool> is = sieve(n);
vector<int> primes;
for(int i = 1; i <= n; i++){
if(is[i]){
primes.push_back(i);
}
}
return primes;
}
```
- Otra forma es obtenerlos al mismo tiempo que tachamos los números. Del algoritmo 3 cada que detectemos un primo en la línea 8, simplemente lo guardamos en el arreglo de primos. Solo que ahora habría que cambiar el límite del `for` de la línea 7 a `i <= n` para guardar todos los primos, y verificar que `j = i*i` de la línea 9 no se vaya a desbordar:
### Algoritmo 5: obtener primos al mismo tiempo
```cpp=
vector<int> getPrimes(int n){
vector<bool> is(n+1, true);
vector<int> primes = {2}; // introducimos el 2 a mano, pues solo procesamos los primos impares por la optimización
is[0] = is[1] = false;
for(int i = 4; i <= n; i += 2){
is[i] = false;
}
for(int i = 3; i <= n; i += 2){
if(is[i]){
primes.push_back(i); // como i es primo, lo introducimos al arreglo
if((long long)i*i <= n){ // chequeo adicional para evitar desbordar i*i
for(int j = i*i; j <= n; j += 2*i){
is[j] = false;
}
}
}
}
return primes; // ahora devolvemos el arreglo de primos
}
```
## Criba de divisores
Ahora tenemos el siguiente problema: hallar el número de divisores para cada número en el rango $[1, n]$. Una forma de hacerlo es usar el método `getDivisors()` de la sección pasada para cada número, obteniendo una complejidad de $O(n \sqrt{n})$.
Pero podemos usar una idea similar a la criba de primos: para cada $i$ en el rango $[1, n]$ vamos a sumarle 1 a cada múltiplo de $i$ que no exceda $n$, esta vez comenzando desde $i$.
:::info
:information_source: **Ejemplo:** si queremos saber cuántos divisores tiene cada número entre $1$ y $7$, realizamos los siguientes pasos:
- Al principio tenemos el arreglo inicializado en ceros:
$$
\begin{array}{|c|c|c|c|c|c|c|c|}
\hline
n & 1 & 2 & 3 & 4 & 5 & 6 & 7 \\
\hline
divs[n] & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\
\hline
\end{array}
$$
- Le sumamos 1 a todos los múltiplos de $1$: $1, 2, 3, 4, 5, 6, 7$:
$$
\begin{array}{|c|c|c|c|c|c|c|c|}
\hline
n & 1 & 2 & 3 & 4 & 5 & 6 & 7 \\
\hline
divs[n] & 1 & 1 & 1 & 1 & 1 & 1 & 1 \\
\hline
\end{array}
$$
- Le sumamos 1 a todos los múltiplos de $2$: $2, 4, 6$:
$$
\begin{array}{|c|c|c|c|c|c|c|c|}
\hline
n & 1 & 2 & 3 & 4 & 5 & 6 & 7 \\
\hline
divs[n] & 1 & 2 & 1 & 2 & 1 & 2 & 1 \\
\hline
\end{array}
$$
- Le sumamos 1 a todos los múltiplos de $3$: $3, 6$:
$$
\begin{array}{|c|c|c|c|c|c|c|c|}
\hline
n & 1 & 2 & 3 & 4 & 5 & 6 & 7 \\
\hline
divs[n] & 1 & 2 & 2 & 2 & 1 & 3 & 1 \\
\hline
\end{array}
$$
- A partir del $4$ solo habrá un múltiplo dentro del rango, por lo tanto, le sumamos 1 al $4, 5, 6, 7$:
$$
\begin{array}{|c|c|c|c|c|c|c|c|}
\hline
n & 1 & 2 & 3 & 4 & 5 & 6 & 7 \\
\hline
divs[n] & 1 & 2 & 2 & 3 & 2 & 4 & 2 \\
\hline
\end{array}
$$
:::
Esto funciona, pues podemos representar a los múltiplos de $i$ como $i*j$. Sea $i_j=i*j$, entonces $i | i_j$, y en cada paso le estamos sumando 1 al número de divisores de $i_j$, que es justamente el divisor $i$.
La implementación de esta idea es muy sencilla:
### Algoritmo 6: criba de número de divisores
```cpp=
vector<int> divsSieve(int n){
vector<int> divs(n+1); // incializamos el arreglo en ceros
for(int i = 1; i <= n; i++){
for(int j = i; j <= n; j += i){ // iteramos por todos los múltiplos de i
divs[j]++;
}
}
return divs;
}
```
Y la complejidad queda como $O(n+n/2+n/3+n/4+\cdots) = O(n(1+1/2+1/3+1/4+\cdots)) = O(n \log n)$.
Podemos modificar esta criba para obtener la suma de divisores en lugar del número de divisores, simplemente cambiando la línea 5 a `divs[j] += i`, o incluso obtener los divisores como tal para cada número en el rango, solo habría que cambiar la línea por `divs[j].push_back(i)` y que `divs` sea ahora un arreglo de arreglos.
## Algunas observaciones
La criba de Eratóstenes también sirve como test de primalidad, pues una vez obtenido el arreglo, contestar las queries sobre si un número es primo o no son en $O(1)$. Sin embargo, hay que tomar en cuenta que esta criba tiene una complejidad lineal en memoria, por lo que si queremos saber si un número alrededor de $10^{14}$ es primo, este método no nos sirve. Aquí lo correcto es usar el test de primalidad optimizado que vimos en la sección anterior. Por lo tanto:
- Si tenemos muchas queries pero con valores alrededor de $10^8$, usamos esta criba con un precálculo de $O(MAX \log \log MAX)$ y con una complejidad de $O(1)$ para cada query.
- Si tenemos pocas queries pero con valores más grandes, no hacemos precálculo y contestamos cada una de ellas en $O(\sqrt{n})$.
Para el problema del número o suma de divisores, aplica el mismo razonamiento.
También podemos optimizar el test de primalidad que corre en $O(\sqrt{n})$ combinándolo con la criba de Eratóstenes:
- Usamos la criba para precalcular los primos hasta $\lfloor \sqrt{MAX} \rfloor$.
- En el método `isPrime()` (algoritmo 5 de la sección anterior), en lugar de iterar por todas las $d$'s en la línea 3, iteramos directamente sobre todos los primos $p$ obtenidos tales que $p^2 \leq n$. Esto permite reducir un poco la complejidad a $O(\sqrt{n}/\log(n))$, y la implementación queda como:
### Algoritmo 7: test de primalidad combinado con la criba
```cpp=
// precalcular solo una vez el arreglo primes con la criba antes de llamar a esta función
bool isPrime(int n){
if(n == 1) return false; // El 1 sigue siendo un caso especial
for(int p : primes){ // Ahora iteramos únicamente sobre los primos
if((long long)p*p > n) break; // Solo nos interesan los primos p tales que p*p <= n
if(n % p == 0){
return false; // Encontramos un divisor primo de n que no es ni 1 ni n
}
}
return true; // Si llegamos hasta este punto, n tiene que ser primo
}
```
## Criba del factor primo más pequeño
Ahora queremos obtener por cada número en el rango $[1,n]$ su factor primo más pequeño. Es decir, el número primo más pequeño en la lista de divisores de $i$, para toda $i \in [1,n]$.
:::info
:information_source: **Ejemplo:** sea $lp[n]$ el factor primo más pequeño de $n$. Entonces:
- $lp[2] = 2$
- $lp[3] = 3$
- $lp[4] = 2$
- $lp[5] = 5$
- $lp[6] = 2$
- $lp[7] = 7$
- $lp[8] = 2$
- $lp[9] = 3$
- $lp[10] = 2$
:::
Algunas propiedades de $lp[n]$ son:
- Si $n$ es par, entonces $lp[n] = 2$.
- Si $n$ es primo, entonces $lp[n] = n$.
- Si $n$ es compuesto, entonces $lp[n]^2 \leq n$.
- $lp[1]$ no está definido.
Para hallar el arreglo $lp[]$, tenemos que realizar una modificación a la criba original: en lugar de guardar el arreglo de marcas $is[]$, lo vamos a reemplazar con el arreglo de enteros $lp[]$, y al principio tendremos que $lp[i]=i$ para toda $i \in [1,n]$. Los cambios son los siguientes:
- En la línea 8, para saber si el número actual $i$ es primo, haremos el chequeo: `if(lp[i] == i)`.
- En la línea 13, ahora poner una marca de número compuesto implica actualizar el factor primo más pequeño: `lp[j] = min(lp[j], i)`.
- Al principio hacemos que $lp[i]=2$ para toda $i$ par, en caso de que decidamos utilizar esta optimización.
:::info
:information_source: **Ejemplo:** veamos una simulación de estas modificaciones para hallar $lp[]$ de $1$ a $20$ (sin la optimización de números pares):
Al principio nuestro arreglo se inicializa así (el valor de $lp[1]$ puede valer lo que sea):
$$
\require{cancel}
\begin{array}{|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|}
\hline
n & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15 & 16 & 17 & 18 & 19 & 20 \\
\hline
lp[n] & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15 & 16 & 17 & 18 & 19 & 20 \\
\hline
\end{array}
$$
Tenemos que $lp[2]=2$, por lo tanto $2$ es primo y actualizamos todos sus múltiplos mayores:
$$
\require{cancel}
\begin{array}{|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|}
\hline
n & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15 & 16 & 17 & 18 & 19 & 20 \\
\hline
lp[n] & 1 & 2 & 3 & \color{red}{2} & 5 & \color{red}{2} & 7 & \color{red}{2} & 9 & \color{red}{2} & 11 & \color{red}{2} & 13 & \color{red}{2} & 15 & \color{red}{2} & 17 & \color{red}{2} & 19 & \color{red}{2} \\
\hline
\end{array}
$$
Tenemos que $lp[3]=3$, por lo tanto $3$ es primo y actualizamos todos sus múltiplos mayores:
$$
\require{cancel}
\begin{array}{|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|}
\hline
n & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15 & 16 & 17 & 18 & 19 & 20 \\
\hline
lp[n] & 1 & 2 & 3 & 2 & 5 & 2 & 7 & 2 & \color{red}{3} & 2 & 11 & 2 & 13 & 2 & \color{red}{3} & 2 & 17 & 2 & 19 & 2 \\
\hline
\end{array}
$$
Tenemos que $lp[4] \neq 4$, por lo tanto $4$ no es primo.
Comno $5^2 > 20$, terminamos el algoritmo y el arreglo $lp[]$ queda como la última iteración.
:::
Tener precalculados estos primos más pequeños nos será de mucha ayuda para un algoritmo de factorización de la siguiente sección.
Modificando el algoritmo 3, la implementación queda como:
### Algoritmo 8: criba del factor primo más pequeño
```cpp=
vector<int> lpSieve(int n){
vector<int> lp(n+1); // arreglo del factor primo más pequeño
iota(lp.begin(), lp.end(), 0); // usamos la STL para rellenar el arreglo inicial
for(int i = 4; i <= n; i += 2){
lp[i] = 2; // 2 es el factor primo más pequeño de todos los números pares
}
for(int i = 3; i*i <= n; i += 2){ // comenzamos a iterar por los impares desde el 3
if(lp[i] == i){ // si el número actual es primo, actualizamos todos sus múltiplos mayores
for(int j = i*i; j <= n; j += 2*i){ // comenzamos en i*i y solo iteramos por los impares
lp[j] = min(lp[j], i);
}
}
}
return lp;
}
```
## Criba del factor primo más grande
Ahora hallaremos el arreglo $gp[]$, donde $gp[i]$ nos dice el divisor primo más grande de $i$.
:::info
:information_source: **Ejemplo:** Tenemos los siguientes valores para el arreglo $gp[]$:
- $gp[2] = 2$
- $gp[3] = 3$
- $gp[4] = 2$
- $gp[5] = 5$
- $gp[6] = 3$
- $gp[7] = 7$
- $gp[8] = 2$
- $gp[9] = 3$
- $gp[10] = 5$
:::
A diferencia del factor primo más pequeño, el factor primo más grande solo cumple que si $n$ es primo, entonces $gp[n] = n$. Por lo tanto, podemos seguir usando la condición `if(gp[i] == i)` para verificar si $i$ es primo, pero ya no podemos usar la optimización de `i*i <= n`, pues ahora no siempre tendremos que $gp[n]^2 \leq n$, ni la de los números pares. Las actualizaciones ahora las haremos con `gp[j] = i`, pues como iteramos por todas las $i$'s en orden, siempre iremos guardando el primo más grande que divide a $j$.
La implementación queda como:
### Algoritmo 9: criba del factor primo más grande
```cpp=
vector<int> gpSieve(int n){
vector<int> gp(n+1); // arreglo del factor primo más grande
iota(gp.begin(), gp.end(), 0); // usamos la STL para rellenar el arreglo inicial
for(int i = 2; i <= n; i++){
if(gp[i] == i){ // si el número actual es primo, actualizamos todos sus múltiplos mayores
for(int j = 2*i; j <= n; j += i){
gp[j] = i; // al siempre actualizar los múltiplos, garantizamos siempre guardar el factor primo más grande
}
}
}
return gp;
}
```
## Criba segmentada
## Hallar primos en un rango