Cómo leer fórmulas matemáticas en algoritmos
Muchos programadores sienten rechazo o inseguridad frente a enunciados repletos de símbolos griegos, sumatorias dobles o notación formal.
La realidad es simple: la notación matemática formal es pseudocódigo comprimido. Una vez que aprendés a mapear cada símbolo a su estructura de control equivalente (bucles for, acumuladores, condicionales if y arreglos), leer fórmulas se vuelve tan directo como leer código.
1. Sumatorias () Bucles for con Acumulador
El símbolo sigma mayúscula representa una suma acumulada de términos sobre un rango de índices.
Sumatoria simple
// Arreglo 'a' de tamaño n + 1 (1-indexed)
long long suma = 0;
for (int i = 1; i <= n; i++) {
suma += a[i];
}# Lista 'a' de tamaño n + 1 (1-indexed)
suma = sum(a[i] for i in range(1, n + 1))Sumatoria doble (Bucles anidados)
long long total = 0;
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
total += (a[i] + a[j]);
}
}total = 0
for i in range(1, n + 1):
for j in range(i, n + 1):
total += (a[i] + a[j])Sumatoria condicional (Notación de corchetes de Iverson )
El corchete vale si la condición es verdadera y si es falsa. En código equivale a un condicional if:
long long suma_positivos = 0;
for (int i = 1; i <= n; i++) {
if (a[i] > 0) {
suma_positivos += a[i];
}
}suma_positivos = sum(a[i] for i in range(1, n + 1) if a[i] > 0)2. Productorias () Acumulador Multiplicativo
El símbolo pi mayúscula representa el producto de una secuencia de factores.
const long long MOD = 1e9 + 7;
long long producto = 1;
for (int i = 1; i <= n; i++) {
producto = (producto * i) % MOD; // Módulo preventivo contra desbordes
}MOD = 10**9 + 7
producto = 1
for i in range(1, n + 1):
producto = (producto * i) % MOD3. Aritmética Modular () Operador %
La expresión (se lee “a es congruente con b módulo m”) significa que y dejan el mismo resto al dividirse por , o equivalentemente, que es divisible por .
Propiedades algebraicas en código
Definimos la constante usual de competencias:
const long long MOD = 1e9 + 7;- Suma modular: .
- Resta modular: . (El es crucial en C++ y Java para garantizar que el resultado no sea negativo).
- Multiplicación modular: .
Inverso Modular ()
En aritmética modular no existe la división directa. Para calcular , multiplicamos por el inverso modular de :
Por el Pequeño Teorema de Fermat, si es un número primo y no es múltiplo de ():
#include <cassert>
const long long MOD = 1e9 + 7;
long long power(long long base, long long exp, long long mod = MOD) {
long long res = 1;
base %= mod;
while (exp > 0) {
if (exp % 2 == 1) res = (res * base) % mod;
base = (base * base) % mod;
exp /= 2;
}
return res;
}
long long modInverse(long long b, long long mod = MOD) {
long long b_mod = ((b % mod) + mod) % mod;
assert(b_mod != 0 && "El inverso modular no existe para múltiplos de mod");
return power(b_mod, mod - 2, mod);
}MOD = 10**9 + 7
def power(base: int, exp: int, mod: int = MOD) -> int:
return pow(base, exp, mod)
def mod_inverse(b: int, mod: int = MOD) -> int:
b_mod = b % mod
assert b_mod != 0, "El inverso modular no existe para múltiplos de mod"
return pow(b_mod, mod - 2, mod)[!NOTE] En C++,
res * baseentra en unlong longsiempre quemod(ya que ). Para módulos mayores a se debe recurrir a__int128. En Python los enteros tienen precisión arbitraria automática.
4. Coeficientes Binomiales () Combinatoria “n en k”
El símbolo (se lee “n en k” o “número combinatorio”) representa la cantidad de subconjuntos de elementos que se pueden formar a partir de un conjunto de elementos:
Identidad de Pascal (Base de Programación Dinámica)
const int MAXN = 1000;
const long long MOD = 1e9 + 7;
// Tabla de (MAXN + 1) x (MAXN + 1): ocupa ~8 MB de memoria
long long C[MAXN + 1][MAXN + 1];
void build_pascal() {
for (int n = 0; n <= MAXN; n++) {
C[n][0] = C[n][n] = 1;
for (int k = 1; k < n; k++) {
C[n][k] = (C[n - 1][k - 1] + C[n - 1][k]) % MOD;
}
}
}MAXN = 1000
MOD = 10**9 + 7
C = [[0] * (MAXN + 1) for _ in range(MAXN + 1)]
def build_pascal():
for n in range(MAXN + 1):
C[n][0] = C[n][n] = 1
for k in range(1, n):
C[n][k] = (C[n - 1][k - 1] + C[n - 1][k]) % MOD5. Funciones Piso () y Techo () División Entera
- Piso (): Redondea hacia abajo al entero menor o igual (, ).
- Techo (): Redondea hacia arriba al entero mayor o igual ().
Cálculo de Techo sin usar Punto Flotante
Para y :
-
Fórmula estándar:
En código:
(a + b - 1) / b -
Alternativa segura contra desborde: Si puede estar cerca del valor máximo de
long long(LLONG_MAX), sumar causaría un integer overflow. En ese caso, usá:En código:
a / b + (a % b != 0)
6. Notación Asintótica () vs Escenarios de Entrada
Ejemplo con Quicksort clásico:
- El peor caso de Quicksort es (y por ende también ).
- El mejor caso de Quicksort es (y por ende también ).
- El caso promedio es .
Tabla de referencia: Complejidad vs para 1 Segundo ( operaciones)
| Máximo | Complejidad Requerida | Algoritmos Típicos |
|---|---|---|
Búsqueda exhaustiva sobre permutaciones (std::next_permutation). | ||
| Búsqueda sobre todos los subconjuntos, DP con máscaras de bits (Bitmask DP). | ||
| Algoritmo de Floyd-Warshall, multiplicación clásica de matrices. | ||
| Programación Dinámica 2D, ordenamientos simples, bucles anidados. | ||
| Ordenamientos eficientes (MergeSort), Segment Tree, Búsqueda Binaria, Dijkstra. | ||
| Dos Punteros, Sumas de Prefijos, Criba de Eratóstenes. | ||
| o | Exponenciación binaria, MCD de Euclides, fórmulas algebraicas cerradas. |
7. Conjuntos, Lógica Formal y Operadores Bitwise
Pertenencia y Cuantificadores
- : pertenece al conjunto (
S.contains(x)en C++20,S.count(x) > 0en C++ clásico ox in Sen Python). - : es subconjunto de .
- : Unión de conjuntos.
- : Intersección de conjuntos.
- : Diferencia de conjuntos (elementos en que no están en ).
- : “Para todo elemento de ” (bucle donde todos deben cumplir la condición).
- : “Existe al menos un elemento en ” (bucle con interrupción al hallar el primero).
- : “El conjunto de elementos de que cumplen el predicado ”.
Operadores Lógicos vs Operadores Bitwise
| Operación | Símbolo Matemático | Operador Lógico (Booleano) | Operador Bit a Bit (Bitwise) |
|---|---|---|---|
| Negación / NOT | !a (da true o false) | ~a (invierte bits; en complemento a dos da ~a == -a - 1) | |
| Conjunción / AND | a && b | a & b | |
| Disyunción / OR | a || b | a | b | |
| OR Exclusivo / XOR | a != b (para booleanos) | a ^ b |
8. Recurrencias de Programación Dinámica
Las fórmulas de DP definen el valor de un estado a partir de subproblemas más pequeños.
Ejemplo: Problema de la Mochila 0/1
Desglose paso a paso:
- Dimensiones del estado: es la ganancia máxima considerando los primeros objetos con capacidad restante .
- Caso 1 (): El objeto es demasiado pesado para la capacidad actual; obligatoriamente se ignora ().
- Caso 2 (): Podemos elegir entre no tomarlo () o tomarlo (). La función selecciona la opción más rentable.
// peso y valor están 1-indexed (tamaño n + 1)
vector<vector<long long>> dp(n + 1, vector<long long>(W + 1, 0));
for (int i = 1; i <= n; i++) {
for (int w = 0; w <= W; w++) {
if (w < peso[i]) {
dp[i][w] = dp[i - 1][w];
} else {
dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - peso[i]] + valor[i]);
}
}
}# peso y valor están 1-indexed (tamaño n + 1)
dp = [[0] * (W + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for w in range(W + 1):
if w < peso[i]:
dp[i][w] = dp[i - 1][w]
else:
dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - peso[i]] + valor[i])9. Álgebra de Bits en Fórmulas
- : Potencia de 2 Corrimiento de bits a la izquierda:
1LL << ken C++ o1 << ken Python. - Representación binaria como sumatoria:
- Cantidad de bits encendidos (popcount):
- En C++20:
std::popcount(static_cast<unsigned long long>(x))(incluyendo<bit>). - En C++ clásico (GCC/Clang):
__builtin_popcountll(x). - En Python:
x.bit_count()(en Python 3.10+) obin(x).count("1")(versiones anteriores).
- En C++20:
10. Método en 4 Pasos para Programar Cualquier Fórmula
- Paso 1: Tipos de datos e indexación: Verificá rangos numéricos. Si los resultados intermedios exceden , utilizá
long long. Declarar arreglos con tamaño para mantener coincidencia con los índices de la fórmula. - Paso 2: Localizar el caso base: ¿Qué valor toma la expresión cuando el índice vale o ? Esos valores inicializan tus arreglos o variables acumuladoras.
- Paso 3: Estructura de bucles: Mapeá cada sumatoria o cuantificador exterior a un bucle
for, y colocá la expresión central en el cuerpo más interno. - Paso 4: Factorización y optimización: Buscá si la fórmula permite separar variables independientes para reducir la complejidad temporal. Por ejemplo: Transforma un algoritmo de a precalculando sumas parciales.