Skip to Content

Coeficientes binomiales

Los coeficientes binomiales (binomial coefficients) (nk)\binom n k son el número de formas de seleccionar un conjunto de kk elementos de nn elementos distintos sin tener en cuenta el orden de disposición de estos elementos (es decir, el número de conjuntos no ordenados).

Los coeficientes binomiales son también los coeficientes en la expansión de (a+b)n(a + b) ^ n (el llamado teorema del binomio):

(a+b)n=(n0)an+(n1)an1b+(n2)an2b2++(nk)ankbk++(nn)bn (a+b)^n = \binom n 0 a^n + \binom n 1 a^{n-1} b + \binom n 2 a^{n-2} b^2 + \cdots + \binom n k a^{n-k} b^k + \cdots + \binom n n b^n

Se cree que esta fórmula, así como el triángulo que permite calcular de forma eficiente los coeficientes, fue descubierta por Blaise Pascal en el siglo XVII. No obstante, ya la conocía el matemático chino Yang Hui, que vivió en el siglo XIII. Quizá la descubrió el sabio persa Omar Khayyam. Además, el matemático indio Pingala, que vivió antes, en el siglo III a. C., obtuvo resultados similares. El mérito de Newton es que generalizó esta fórmula para exponentes que no son naturales.

Cálculo

Fórmula analítica para el cálculo:

(nk)=n!k!(nk)! \binom n k = \frac {n!} {k!(n-k)!}

Esta fórmula se puede deducir fácilmente del problema de las disposiciones ordenadas (número de formas de seleccionar kk elementos distintos de nn elementos distintos). Primero, contemos el número de selecciones ordenadas de kk elementos. Hay nn formas de seleccionar el primer elemento, n1n-1 formas de seleccionar el segundo, n2n-2 formas de seleccionar el tercero, y así sucesivamente. Como resultado, obtenemos la fórmula del número de disposiciones ordenadas: n(n1)(n2)(nk+1)=n!(nk)!n (n-1) (n-2) \cdots (n - k + 1) = \frac {n!} {(n-k)!}. Podemos pasar fácilmente a las disposiciones no ordenadas, observando que cada disposición no ordenada corresponde a exactamente k!k! disposiciones ordenadas (k!k! es el número de permutaciones posibles de kk elementos). Obtenemos la fórmula final dividiendo n!(nk)!\frac {n!} {(n-k)!} por k!k!.

Fórmula de recurrencia (asociada al famoso “triángulo de Pascal”):

(nk)=(n1k1)+(n1k) \binom n k = \binom {n-1} {k-1} + \binom {n-1} k

Es fácil deducirla usando la fórmula analítica.

Nótese que para n<kn \lt k se asume que el valor de (nk)\binom n k es cero.

Propiedades

Los coeficientes binomiales tienen muchas propiedades distintas. Aquí están las más simples:

  • Regla de simetría:

    (nk)=(nnk)\binom n k = \binom n {n-k}

  • Extracción de un factor:

    (nk)=nk(n1k1)\binom n k = \frac n k \binom {n-1} {k-1}

  • Suma sobre kk:

    k=0n(nk)=2n\sum_{k = 0}^n \binom n k = 2 ^ n

  • Suma sobre nn:

    m=0n(mk)=(n+1k+1)\sum_{m = 0}^n \binom m k = \binom {n + 1} {k + 1}

  • Suma sobre nn y kk:

    k=0m(n+kk)=(n+m+1m)\sum_{k = 0}^m \binom {n + k} k = \binom {n + m + 1} m

  • Suma de los cuadrados:

    (n0)2+(n1)2++(nn)2=(2nn){\binom n 0}^2 + {\binom n 1}^2 + \cdots + {\binom n n}^2 = \binom {2n} n

  • Suma ponderada:

    1(n1)+2(n2)++n(nn)=n2n11 \binom n 1 + 2 \binom n 2 + \cdots + n \binom n n = n 2^{n-1}

  • Relación con los números de Fibonacci:

    (n0)+(n11)++(nkk)++(0n)=Fn+1\binom n 0 + \binom {n-1} 1 + \cdots + \binom {n-k} k + \cdots + \binom 0 n = F_{n+1}

Cálculo

Cálculo directo usando la fórmula analítica

La primera fórmula, la directa, es muy fácil de programar, pero es probable que este método desborde incluso para valores relativamente pequeños de nn y kk (incluso si la respuesta cabe por completo en algún tipo de dato, el cálculo de los factoriales intermedios puede provocar desbordamiento). Por lo tanto, este método a menudo solo se puede usar con aritmética de grandes números:

int C(int n, int k) { int res = 1; for (int i = n - k + 1; i <= n; ++i) res *= i; for (int i = 2; i <= k; ++i) res /= i; return res; }

Implementación mejorada

Nótese que en la implementación anterior el numerador y el denominador tienen la misma cantidad de factores (kk), cada uno de los cuales es mayor o igual que 1. Por lo tanto, podemos reemplazar nuestra fracción por un producto de kk fracciones, cada una de las cuales es de valor real. Sin embargo, en cada paso, después de multiplicar la respuesta actual por cada una de las fracciones siguientes, la respuesta seguirá siendo entera (esto se sigue de la propiedad de extracción de un factor).

Implementación en C++:

int C(int n, int k) { double res = 1; for (int i = 1; i <= k; ++i) res = res * (n - k + i) / i; return (int)(res + 0.01); }

Acá casteamos con cuidado el número de punto flotante a un entero, teniendo en cuenta que, debido a los errores acumulados, puede quedar ligeramente por debajo del valor verdadero (por ejemplo, 2.999992.99999 en lugar de 33).

Triángulo de Pascal

Usando la relación de recurrencia podemos construir una tabla de coeficientes binomiales (triángulo de Pascal) y tomar el resultado de ella. La ventaja de este método es que los resultados intermedios nunca superan la respuesta y calcular cada nuevo elemento de la tabla requiere solo una suma. La desventaja es la ejecución lenta para nn y kk grandes si solo se necesita un único valor y no la tabla completa (porque para calcular (nk)\binom n k habrá que construir una tabla de todos los (ij),1in,1jn\binom i j, 1 \le i \le n, 1 \le j \le n, o al menos hasta 1jmin(i,2k)1 \le j \le \min (i, 2k)). La complejidad temporal se puede considerar O(n2)\mathcal{O}(n^2).

Implementación en C++:

const int maxn = ...; int C[maxn + 1][maxn + 1]; C[0][0] = 1; for (int n = 1; 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]; }

Si no hace falta la tabla completa de valores, basta con guardar solo las dos últimas filas (la fila nn-ésima actual y la n1n-1-ésima anterior).

Cálculo en O(1)O(1) {data-toc-label=“Cálculo en O(1)”}

Por último, en algunas situaciones conviene precalcular todos los factoriales para producir después cualquier coeficiente binomial necesario con solo dos divisiones. Esto puede ser ventajoso al usar aritmética de grandes números, cuando la memoria no permite precalcular todo el triángulo de Pascal.

Cálculo de coeficientes binomiales módulo mm {data-toc-label=“Cálculo de coeficientes binomiales módulo m”}

Con bastante frecuencia aparece el problema de calcular coeficientes binomiales módulo algún mm.

Coeficiente binomial para nn pequeño {data-toc-label=“Coeficiente binomial para n pequeño”}

El enfoque del triángulo de Pascal discutido antes se puede usar para calcular todos los valores de (nk)modm\binom{n}{k} \bmod m para nn razonablemente pequeño, ya que requiere complejidad temporal O(n2)\mathcal{O}(n^2). Este enfoque puede manejar cualquier módulo, porque solo se usan operaciones de suma.

Coeficiente binomial módulo un primo grande

La fórmula de los coeficientes binomiales es

(nk)=n!k!(nk)!,\binom n k = \frac {n!} {k!(n-k)!},

así que si queremos calcularlo módulo algún primo m>nm > n obtenemos

(nk)n!(k!)1((nk)!)1modm.\binom n k \equiv n! \cdot (k!)^{-1} \cdot ((n-k)!)^{-1} \mod m.

Primero precalculamos todos los factoriales módulo mm hasta MAXN!\text{MAXN}! en tiempo O(MAXN)O(\text{MAXN}).

factorial[0] = 1; for (int i = 1; i <= MAXN; i++) { factorial[i] = factorial[i - 1] * i % m; }

Y después podemos calcular el coeficiente binomial en tiempo O(logm)O(\log m).

long long binomial_coefficient(int n, int k) { return factorial[n] * inverse(factorial[k] * factorial[n - k] % m) % m; }

Incluso podemos calcular el coeficiente binomial en tiempo O(1)O(1) si precalculamos los inversos de todos los factoriales en O(MAXNlogm)O(\text{MAXN} \log m) usando el método habitual para calcular el inverso, o incluso en tiempo O(MAXN)O(\text{MAXN}) usando la congruencia (x!)1((x1)!)1x1(x!)^{-1} \equiv ((x-1)!)^{-1} \cdot x^{-1} y el método para calcular todos los inversos en O(n)O(n).

long long binomial_coefficient(int n, int k) { return factorial[n] * inverse_factorial[k] % m * inverse_factorial[n - k] % m; }

Coeficiente binomial módulo una potencia de primo { #mod-prime-pow}

Acá queremos calcular el coeficiente binomial módulo alguna potencia de primo, es decir m=pbm = p^b para algún primo pp. Si p>max(k,nk)p > \max(k, n-k), entonces podemos usar el mismo método descrito en la sección anterior. Pero si pmax(k,nk)p \le \max(k, n-k), entonces al menos uno de k!k! y (nk)!(n-k)! no es coprimo con mm, y por lo tanto no podemos calcular los inversos: no existen. No obstante, podemos calcular el coeficiente binomial.

La idea es la siguiente: Para cada x!x! calculamos el mayor exponente cc tal que pcp^c divide a x!x!, es decir pc  x!p^c | x!. Sea c(x)c(x) ese número. Y sea g(x):=x!pc(x)g(x) := \frac{x!}{p^{c(x)}}. Entonces podemos escribir el coeficiente binomial como:

(nk)=g(n)pc(n)g(k)pc(k)g(nk)pc(nk)=g(n)g(k)g(nk)pc(n)c(k)c(nk)\binom n k = \frac {g(n) p^{c(n)}} {g(k) p^{c(k)} g(n-k) p^{c(n-k)}} = \frac {g(n)} {g(k) g(n-k)}p^{c(n) - c(k) - c(n-k)}

Lo interesante es que g(x)g(x) ahora está libre del divisor primo pp. Por lo tanto g(x)g(x) es coprimo con mm, y podemos calcular los inversos modulares de g(k)g(k) y g(nk)g(n-k).

Después de precalcular todos los valores de gg y cc, lo que se puede hacer de forma eficiente con programación dinámica en O(n)\mathcal{O}(n), podemos calcular el coeficiente binomial en tiempo O(logm)O(\log m). O precalcular todos los inversos y todas las potencias de pp, y entonces calcular el coeficiente binomial en O(1)O(1).

Nótese que, si c(n)c(k)c(nk)bc(n) - c(k) - c(n-k) \ge b, entonces pb  pc(n)c(k)c(nk)p^b | p^{c(n) - c(k) - c(n-k)}, y el coeficiente binomial es 00.

Coeficiente binomial módulo un número arbitrario

Ahora calculamos el coeficiente binomial módulo algún módulo arbitrario mm.

Sea la factorización en primos de mm igual a m=p1e1p2e2phehm = p_1^{e_1} p_2^{e_2} \cdots p_h^{e_h}. Podemos calcular el coeficiente binomial módulo pieip_i^{e_i} para cada ii. Esto nos da hh congruencias distintas. Como todos los módulos pieip_i^{e_i} son coprimos, podemos aplicar el Teorema Chino del Resto para calcular el coeficiente binomial módulo el producto de los módulos, que es el coeficiente binomial deseado módulo mm.

Coeficiente binomial para nn grande y módulo pequeño {data-toc-label=“Coeficiente binomial para n grande y módulo pequeño”}

Cuando nn es demasiado grande, los algoritmos O(n)\mathcal{O}(n) discutidos arriba se vuelven impracticables. Sin embargo, si el módulo mm es pequeño todavía hay formas de calcular (nk)modm\binom{n}{k} \bmod m.

Cuando el módulo mm es primo, hay 2 opciones:

  • Se puede aplicar el teorema de Lucas , que parte el problema de calcular (nk)modm\binom{n}{k} \bmod m en logmn\log_m n problemas de la forma (xiyi)modm\binom{x_i}{y_i} \bmod m donde xi,yi<mx_i, y_i < m. Si cada coeficiente reducido se calcula usando factoriales e inversos factoriales precalculados, la complejidad es O(m+logmn)\mathcal{O}(m + \log_m n).
  • Se puede usar el método para calcular el factorial módulo P para obtener los valores de gg y cc requeridos y usarlos como se describe en la sección de módulo potencia de primo. Esto toma O(mlogmn)\mathcal{O}(m \log_m n).

Cuando mm no es primo pero es libre de cuadrados, se pueden obtener los factores primos de mm y calcular el coeficiente módulo cada factor primo con cualquiera de los métodos anteriores, y la respuesta global se obtiene con el Teorema Chino del Resto.

Cuando mm no es libre de cuadrados, se puede aplicar una generalización del teorema de Lucas para potencias de primo  en lugar del teorema de Lucas.

Problemas de práctica

Referencias