Universidad Central del Ecuador · Facultad de Ciencias Económicas · Carrera de Estadística
Asignatura: Programación
El álgebra amplía la aritmética al sustituir números concretos por símbolos que representan cantidades desconocidas o variables. Gracias a esa generalización es posible expresar relaciones entre magnitudes y razonar sobre ellas antes de conocer sus valores particulares (Baldor, 2007). La herramienta central de este razonamiento es la ecuación: una igualdad que solo se cumple para ciertos valores de la incógnita, cuya búsqueda constituye el problema de resolverla.
Entre los distintos tipos de ecuaciones, dos ocupan un lugar privilegiado en la formación universitaria. Las ecuaciones de segundo grado describen fenómenos en los que una magnitud crece y luego decrece, o viceversa, como la trayectoria de un proyectil, el área de una figura en función de uno de sus lados o el ingreso de una empresa cuando el precio afecta a la cantidad vendida (Stewart et al., 2012). Los sistemas de ecuaciones lineales, por su parte, aparecen cuando varias condiciones deben cumplirse al mismo tiempo: mezclas, repartos de recursos o equilibrios de mercado (Grossman y Flores Godoy, 2012). Desde la perspectiva estadística su importancia es aún mayor, porque el ajuste de una recta de regresión por mínimos cuadrados se reduce, precisamente, a resolver un sistema de ecuaciones lineales.
Resolver estos problemas a mano es indispensable para comprender su lógica; sin embargo, cuando los coeficientes son decimales, cuando hay muchas ecuaciones o cuando se requiere verificar resultados, la programación se vuelve una aliada natural. R es un lenguaje y entorno libre para computación estadística y gráficos (R Core Team, 2024) que incorpora aritmética de números complejos, álgebra matricial y funciones gráficas de alta calidad, a las que se suma el paquete ggplot2 (Wickham, 2016). Además, R Markdown permite reunir en un solo documento el texto explicativo, el código y sus resultados, lo que garantiza que cada cifra presentada provenga de una ejecución real y pueda reproducirse (Xie, 2015; Xie et al., 2018).
El objetivo general de esta publicación es explicar la resolución de ecuaciones de segundo grado y de sistemas de ecuaciones lineales de dos incógnitas, de modo que el lector pueda resolverlos manualmente y, después, comprobar y automatizar sus soluciones con R.
Para cumplir ese objetivo, el desarrollo se organiza en dos bloques. El primero presenta los fundamentos de las ecuaciones de segundo grado, el papel del discriminante y su resolución en R, y luego resuelve tres ejercicios que cubren los tres casos posibles del discriminante. El segundo bloque aborda los sistemas de dos ecuaciones con dos incógnitas, sus métodos de resolución y su representación matricial, y resuelve un sistema con solución única, uno sin solución y otro con infinitas soluciones. Cada ejercicio sigue la misma secuencia: planteamiento, procedimiento matemático, código en R, resultado de la ejecución e interpretación. El documento concluye con aplicaciones prácticas que muestran las ventajas de combinar el álgebra con la programación.
Antes de empezar, se cargan los paquetes que se utilizarán a lo largo del documento. ggplot2 construye los gráficos, mientras que knitr y kableExtra dan formato a las tablas. La función tabla_formato() reúne en una sola instrucción el formato que comparten todas las tablas del documento.
library(ggplot2) # gráficos de parábolas y rectas
library(knitr) # función kable() para tablas
library(kableExtra) # estilo de las tablas en el PDF
# Da formato uniforme a las tablas: en el PDF se fijan la posición y el ancho
tabla_formato <- function(datos, titulo) {
tabla <- kable(datos, caption = titulo, booktabs = TRUE)
if (knitr::is_latex_output()) {
tabla <- kable_styling(tabla,
latex_options = c("hold_position", "scale_down"))
}
tabla
}Una ecuación de segundo grado, o ecuación cuadrática, con una incógnita es toda igualdad que puede escribirse en la forma general
\[ ax^2 + bx + c = 0, \qquad a \neq 0, \]
donde \(x\) es la incógnita y \(a\), \(b\) y \(c\) son números reales llamados coeficientes (Baldor, 2007). La condición \(a \neq 0\) es esencial: si \(a\) fuera cero, desaparecería el término cuadrático y la ecuación sería de primer grado.
Cada coeficiente cumple una función precisa. El coeficiente cuadrático \(a\) determina la forma de la parábola \(y = ax^2 + bx + c\): si es positivo, la parábola se abre hacia arriba; si es negativo, hacia abajo; y cuanto mayor es su valor absoluto, más cerrada resulta. El coeficiente lineal \(b\), junto con \(a\), fija la posición del eje de simetría, que es la recta \(x = -b/(2a)\). Finalmente, el término independiente \(c\) indica el punto donde la parábola corta al eje vertical, \((0, c)\).
Esta lectura gráfica es la clave de todo el tema: resolver \(ax^2 + bx + c = 0\) equivale a encontrar los valores de \(x\) en los que la parábola corta al eje \(X\), porque en esos puntos la altura \(y\) vale cero (Stewart et al., 2012).
La fórmula general se obtiene completando el cuadrado. Se divide la ecuación entre \(a\), se traslada el término independiente y se suma a ambos lados el cuadrado de la mitad del coeficiente de \(x\):
\[ \begin{aligned} x^2 + \frac{b}{a}x &= -\frac{c}{a} \\ x^2 + \frac{b}{a}x + \frac{b^2}{4a^2} &= \frac{b^2}{4a^2} - \frac{c}{a} \\ \left(x + \frac{b}{2a}\right)^2 &= \frac{b^2 - 4ac}{4a^2} \\ x &= \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}. \end{aligned} \]
La cantidad que aparece bajo la raíz recibe el nombre de discriminante y se denota con la letra griega delta:
\[ \Delta = b^2 - 4ac. \]
Su nombre es preciso, ya que discrimina el tipo de soluciones antes de calcularlas. Como se resume en la Tabla 1, su signo decide si la raíz cuadrada produce dos valores reales distintos, un único valor o números complejos.
| Signo del discriminante | Tipo de soluciones | Interpretación gráfica |
|---|---|---|
| Positivo (mayor que 0) | Dos soluciones reales diferentes | La parábola corta al eje X en dos puntos |
| Igual a 0 | Una solución real doble | La parábola toca al eje X en su vértice |
| Negativo (menor que 0) | Dos soluciones complejas conjugadas | La parábola no corta al eje X |
Cuando el discriminante es negativo, la raíz cuadrada se calcula en el conjunto de los números complejos mediante la unidad imaginaria \(i = \sqrt{-1}\). Las dos soluciones resultan conjugadas, es decir, tienen la misma parte real y partes imaginarias opuestas: \(p + qi\) y \(p - qi\).
La fórmula general resuelve cualquier ecuación cuadrática y por ello es el método de referencia. La factorización es más rápida cuando las raíces son números racionales sencillos. Se apoya en que, si \(x_1\) y \(x_2\) son las soluciones, el polinomio puede escribirse como
\[ ax^2 + bx + c = a(x - x_1)(x - x_2), \]
de modo que, al igualar a cero, basta con anular cada factor. De esta misma identidad se desprenden las relaciones de Cardano-Vieta, \(x_1 + x_2 = -b/a\) y \(x_1 x_2 = c/a\), que constituyen una forma rápida de verificar cualquier resultado.
R no exige una instrucción especial para resolver estas ecuaciones: basta con traducir la fórmula general a código y decidir, mediante condicionales, cuál de los tres casos se presenta. El bloque siguiente define una función propia, resolver_cuadratica(), que valida el coeficiente \(a\), calcula el discriminante y aplica la fórmula correspondiente. La función as.complex() es la pieza que permite obtener raíces complejas, ya que sqrt() aplicada a un número real negativo devuelve NaN.
# Resuelve a*x^2 + b*x + c = 0 y devuelve una lista con los resultados
resolver_cuadratica <- function(a, b, c, tolerancia = 1e-12) {
# Validación: sin término cuadrático la ecuación no es de segundo grado
if (abs(a) < tolerancia) {
stop("El coeficiente a no puede ser cero.")
}
discriminante <- b^2 - 4 * a * c
if (discriminante > tolerancia) {
# Caso 1: dos soluciones reales diferentes
x1 <- (-b + sqrt(discriminante)) / (2 * a)
x2 <- (-b - sqrt(discriminante)) / (2 * a)
raices <- c(x1, x2)
tipo <- "Dos soluciones reales diferentes"
} else if (abs(discriminante) <= tolerancia) {
# Caso 2: una solución real doble
raices <- -b / (2 * a)
tipo <- "Una solución real doble"
} else {
# Caso 3: la raíz del discriminante se calcula como número complejo
raiz_compleja <- sqrt(as.complex(discriminante))
raices <- c((-b + raiz_compleja) / (2 * a),
(-b - raiz_compleja) / (2 * a))
tipo <- "Dos soluciones complejas conjugadas"
}
list(a = a, b = b, c = c,
discriminante = discriminante,
tipo = tipo,
raices = raices)
}La tolerancia evita que errores mínimos de redondeo, propios de la aritmética de punto flotante, hagan que un discriminante teóricamente nulo se interprete como positivo o negativo.
El segundo bloque define dos funciones auxiliares. mostrar_solucion() presenta los resultados de forma legible, y verificar_raices() sustituye cada raíz en el polinomio para confirmar que el resultado es cero. Además, se emplea polyroot(), función nativa de R que calcula las raíces de cualquier polinomio a partir de sus coeficientes ordenados de menor a mayor grado, como segunda comprobación independiente.
# Imprime los resultados de resolver_cuadratica() de forma ordenada
mostrar_solucion <- function(resultado) {
cat("Ecuación:", resultado$a, "x^2 +", resultado$b, "x +",
resultado$c, "= 0\n")
cat("Discriminante:", resultado$discriminante, "\n")
cat("Tipo de solución:", resultado$tipo, "\n")
cat("Raíces:", format(resultado$raices), "\n")
}
# Sustituye las raíces en el polinomio: el resultado debe ser cero
verificar_raices <- function(resultado) {
x <- resultado$raices
valores <- resultado$a * x^2 + resultado$b * x + resultado$c
round(valores, 10) # se redondea para eliminar residuos de cómputo
}Por último, la función graficar_parabola() dibuja la parábola asociada a cada ecuación, marca el vértice y, cuando existen, los puntos de corte con el eje \(X\).
graficar_parabola <- function(resultado, titulo) {
a <- resultado$a; b <- resultado$b; c <- resultado$c
h <- -b / (2 * a) # abscisa del vértice
k <- a * h^2 + b * h + c # ordenada del vértice
# Amplitud del eje X: incluye las raíces reales con un margen
if (is.complex(resultado$raices)) {
semiancho <- 3
} else {
semiancho <- max(abs(resultado$raices - h)) + 1.5
}
curva <- data.frame(x = seq(h - semiancho, h + semiancho,
length.out = 400))
curva$y <- a * curva$x^2 + b * curva$x + c
# Puntos destacados: vértice y, si son reales, las raíces
puntos <- data.frame(x = h, y = k, tipo = "Vértice")
if (!is.complex(resultado$raices)) {
puntos <- rbind(puntos,
data.frame(x = resultado$raices, y = 0,
tipo = "Corte con el eje X"))
}
ggplot(curva, aes(x, y)) +
geom_hline(yintercept = 0, colour = "grey40") +
geom_vline(xintercept = h, linetype = "dotted", colour = "grey50") +
geom_line(colour = "#1f5f8b", linewidth = 1) +
geom_point(data = puntos, aes(colour = tipo), size = 3) +
geom_text(data = puntos,
aes(label = paste0("(", round(x, 3), "; ", round(y, 3), ")")),
vjust = -1, size = 3.3) +
scale_colour_manual(values = c("Vértice" = "#e69f00",
"Corte con el eje X" = "#c0392b")) +
labs(title = titulo,
subtitle = paste("Discriminante =", resultado$discriminante),
x = "x", y = "y", colour = NULL) +
theme_minimal(base_size = 11) +
theme(legend.position = "bottom")
}Planteamiento. Resolver la ecuación \(2x^2 - 7x + 3 = 0\).
Procedimiento matemático. Se identifican los coeficientes \(a = 2\), \(b = -7\) y \(c = 3\), y se calcula el discriminante:
\[ \Delta = (-7)^2 - 4(2)(3) = 49 - 24 = 25 > 0. \]
Como el discriminante es positivo, la ecuación tiene dos soluciones reales diferentes. Al aplicar la fórmula general:
\[ x = \frac{-(-7) \pm \sqrt{25}}{2(2)} = \frac{7 \pm 5}{4} \quad \Longrightarrow \quad x_1 = \frac{12}{4} = 3, \qquad x_2 = \frac{2}{4} = \frac{1}{2}. \]
Por tratarse de raíces racionales, la ecuación también puede resolverse por factorización: \(2x^2 - 7x + 3 = (2x - 1)(x - 3) = 0\), de donde \(x = 1/2\) o \(x = 3\). Las relaciones de Cardano-Vieta confirman el resultado: \(3 + 1/2 = 7/2 = -b/a\) y \(3 \cdot 1/2 = 3/2 = c/a\).
Código en R. Se resuelve la ecuación con la función propia y se compara con polyroot().
#> Ecuación: 2 x^2 + -7 x + 3 = 0
#> Discriminante: 25
#> Tipo de solución: Dos soluciones reales diferentes
#> Raíces: 3.0 0.5
#> [1] 0 0
# Comprobación 2: polyroot() recibe los coeficientes en orden c, b, a
round(polyroot(c(3, -7, 2)), 10)#> [1] 0.5+0i 3.0+0i
Resultado e interpretación. R confirma un discriminante igual a 25 y las raíces \(x_1 = 3\) y \(x_2 = 0.5\), idénticas a las obtenidas manualmente. La sustitución devuelve ceros y polyroot() reproduce las mismas raíces; su parte imaginaria nula indica que son reales. La Figura 1 muestra que la parábola, abierta hacia arriba porque \(a > 0\), corta al eje \(X\) exactamente en esos dos puntos.
Figura 1: Parábola de \(y = 2x^2 - 7x + 3\) con dos cortes en el eje X.
La figura evidencia la relación entre el álgebra y la geometría: el discriminante positivo se traduce en dos intersecciones con el eje horizontal, ubicadas simétricamente respecto de la recta \(x = 7/4\), que contiene al vértice.
Planteamiento. Resolver la ecuación \(4x^2 - 12x + 9 = 0\).
Procedimiento matemático. Con \(a = 4\), \(b = -12\) y \(c = 9\), el discriminante es
\[ \Delta = (-12)^2 - 4(4)(9) = 144 - 144 = 0. \]
El discriminante nulo anula la raíz de la fórmula general, por lo que ambas soluciones coinciden:
\[ x = \frac{-(-12) \pm \sqrt{0}}{2(4)} = \frac{12}{8} = \frac{3}{2}. \]
El resultado se confirma por factorización, ya que el polinomio es un trinomio cuadrado perfecto: \(4x^2 - 12x + 9 = (2x - 3)^2 = 0\), de donde \(x = 3/2\) con multiplicidad dos.
Código en R.
#> Ecuación: 4 x^2 + -12 x + 9 = 0
#> Discriminante: 0
#> Tipo de solución: Una solución real doble
#> Raíces: 1.5
#> [1] 0
#> [1] 1.5+0i 1.5+0i
Resultado e interpretación. La función identifica el caso de solución real doble y devuelve \(x = 1.5\). polyroot() entrega dos veces el mismo valor, lo que refleja la multiplicidad dos de la raíz. Como se observa en la Figura 2, la parábola apenas toca al eje \(X\) en su vértice, el punto \((1.5;\, 0)\).
Figura 2: Parábola de \(y = 4x^2 - 12x + 9\), tangente al eje X en su vértice.
En este caso el vértice y la raíz coinciden. Geométricamente, la solución doble corresponde a la situación límite entre cortar al eje dos veces y no cortarlo.
Planteamiento. Resolver la ecuación \(x^2 + 2x + 5 = 0\).
Procedimiento matemático. Con \(a = 1\), \(b = 2\) y \(c = 5\):
\[ \Delta = 2^2 - 4(1)(5) = 4 - 20 = -16 < 0. \]
Al ser negativo el discriminante, no existen soluciones reales. En el conjunto de los números complejos, \(\sqrt{-16} = 4i\), y la fórmula general da
\[ x = \frac{-2 \pm 4i}{2(1)} = -1 \pm 2i \quad \Longrightarrow \quad x_1 = -1 + 2i, \qquad x_2 = -1 - 2i. \]
La ecuación no puede factorizarse con coeficientes reales, pero sí reescribirse como \((x + 1)^2 + 4 = 0\). Esta forma muestra que el lado izquierdo es siempre mayor o igual que 4 para cualquier \(x\) real, de modo que nunca se anula.
Código en R.
#> Ecuación: 1 x^2 + 2 x + 5 = 0
#> Discriminante: -16
#> Tipo de solución: Dos soluciones complejas conjugadas
#> Raíces: -1+2i -1-2i
#> [1] 0+0i 0+0i
#> [1] -1+2i -1-2i
# Partes real e imaginaria de las raíces
data.frame(raiz = c("x1", "x2"),
parte_real = Re(ec3$raices),
parte_imaginaria = Im(ec3$raices))#> raiz parte_real parte_imaginaria
#> 1 x1 -1 2
#> 2 x2 -1 -2
Resultado e interpretación. R devuelve las raíces \(-1 + 2i\) y \(-1 - 2i\): comparten la parte real \(-1\) y tienen partes imaginarias opuestas, como corresponde a un par conjugado. La sustitución produce cero también en aritmética compleja. La Figura 3 muestra la parábola completamente por encima del eje \(X\), con vértice en \((-1;\, 4)\), por lo que no existe ningún corte.
Figura 3: Parábola de \(y = x^2 + 2x + 5\), sin cortes con el eje X.
Este ejercicio aclara un error frecuente: que la parábola no corte al eje \(X\) no significa que la ecuación carezca de solución, sino que sus soluciones no son números reales. La parte real de las raíces, \(-1\), coincide con la abscisa del vértice.
La Tabla 2 reúne los tres ejercicios y permite comparar cómo el discriminante anticipa el tipo de solución.
# Tabla resumen construida a partir de los objetos calculados
resumen <- data.frame(
"Ecuación" = c("2x² - 7x + 3 = 0", "4x² - 12x + 9 = 0",
"x² + 2x + 5 = 0"),
Discriminante = c(ec1$discriminante, ec2$discriminante,
ec3$discriminante),
Tipo = c(ec1$tipo, ec2$tipo, ec3$tipo),
"Raíces" = c(paste(format(ec1$raices), collapse = " ; "),
paste(format(ec2$raices), collapse = " ; "),
paste(format(ec3$raices), collapse = " ; ")),
check.names = FALSE
)
tabla_formato(resumen, "Resumen de las ecuaciones de segundo grado resueltas")| Ecuación | Discriminante | Tipo | Raíces |
|---|---|---|---|
| 2x² - 7x + 3 = 0 | 25 | Dos soluciones reales diferentes | 3.0 ; 0.5 |
| 4x² - 12x + 9 = 0 | 0 | Una solución real doble | 1.5 |
| x² + 2x + 5 = 0 | -16 | Dos soluciones complejas conjugadas | -1+2i ; -1-2i |
Un sistema de ecuaciones lineales de dos incógnitas es un conjunto de dos ecuaciones de primer grado que deben satisfacerse simultáneamente. Su forma general es
\[ \begin{cases} a_1x + b_1y = c_1 \\ a_2x + b_2y = c_2 \end{cases} \]
donde \(x\) e \(y\) son las incógnitas, es decir, las cantidades desconocidas que se buscan; \(a_1, b_1, a_2, b_2\) son los coeficientes que multiplican a cada incógnita; y \(c_1, c_2\) son los términos independientes. Resolver el sistema consiste en encontrar todos los pares \((x, y)\) que cumplen ambas ecuaciones a la vez (Grossman y Flores Godoy, 2012).
Cada ecuación lineal representa una recta en el plano, de manera que la solución del sistema corresponde a los puntos comunes a las dos rectas. Solo caben tres posiciones relativas: las rectas se cortan en un punto, son paralelas o son coincidentes. Estas posiciones originan los tres tipos de sistemas que se estudian en esta sección.
Los métodos algebraicos clásicos persiguen un mismo fin: eliminar una incógnita para obtener una ecuación con una sola variable (Baldor, 2007).
El sistema puede escribirse de forma compacta como \(AX = B\):
\[ \underbrace{\begin{pmatrix} a_1 & b_1 \\ a_2 & b_2 \end{pmatrix}}_{A} \underbrace{\begin{pmatrix} x \\ y \end{pmatrix}}_{X} = \underbrace{\begin{pmatrix} c_1 \\ c_2 \end{pmatrix}}_{B}, \]
donde \(A\) es la matriz de coeficientes, \(X\) el vector de incógnitas y \(B\) el vector de términos independientes. Si el determinante \(\det(A) = a_1b_2 - a_2b_1\) es distinto de cero, la matriz \(A\) es invertible y la solución única es \(X = A^{-1}B\). De forma equivalente, la regla de Cramer expresa la solución como cociente de determinantes:
\[ x = \frac{\begin{vmatrix} c_1 & b_1 \\ c_2 & b_2 \end{vmatrix}}{\det(A)}, \qquad y = \frac{\begin{vmatrix} a_1 & c_1 \\ a_2 & c_2 \end{vmatrix}}{\det(A)}. \]
Cuando \(\det(A) = 0\), la inversa no existe y es necesario un criterio más general. El teorema de Rouché-Frobenius compara el rango de la matriz de coeficientes \(A\) con el de la matriz ampliada \((A \mid B)\), que se obtiene al añadir a \(A\) la columna de términos independientes (Grossman y Flores Godoy, 2012). La Tabla 3 resume la clasificación resultante para dos incógnitas.
| Condición sobre los rangos | Tipo de sistema | Soluciones | Posición de las rectas |
|---|---|---|---|
| rango(A) = rango(A|B) = 2 | Compatible determinado | Una única solución | Se cortan en un punto |
| rango(A) = rango(A|B) = 1 | Compatible indeterminado | Infinitas soluciones | Coincidentes |
| rango(A) < rango(A|B) | Incompatible | Ninguna solución | Paralelas |
R trabaja de forma nativa con matrices. La función matrix() construye la matriz de coeficientes, det() calcula su determinante, cbind() forma la matriz ampliada y qr() permite obtener el rango de una matriz mediante su descomposición QR. La función solve(A, b) resuelve directamente el sistema, pero tiene una limitación importante: solo funciona cuando la matriz es invertible. Si el sistema no tiene solución o tiene infinitas, solve() se detiene con un error y, además, no distingue entre ambos casos.
Por esa razón, la función resolver_sistema() aplica primero el teorema de Rouché-Frobenius y solo recurre a solve() cuando ha comprobado que la solución es única. Para los sistemas indeterminados, delega en solucion_parametrica(), que expresa el conjunto infinito de soluciones en función de un parámetro.
# Expresa las infinitas soluciones de un sistema 2x2 de rango 1
solucion_parametrica <- function(A, b) {
# Se toma la primera ecuación que no sea nula (0x + 0y)
fila <- which(rowSums(abs(A)) > 0)[1]
a_i <- A[fila, 1]; b_i <- A[fila, 2]; c_i <- b[fila]
if (b_i != 0) {
# Se despeja y: y = pendiente * x + ordenada, con x = t libre
pendiente <- -a_i / b_i
ordenada <- c_i / b_i
signo <- ifelse(ordenada >= 0, " + ", " - ")
expresion <- paste0("x = t, y = ", round(pendiente, 4), "t",
signo, round(abs(ordenada), 4))
generar <- function(t) data.frame(x = t, y = pendiente * t + ordenada)
} else {
# Recta vertical: x queda fija y la incógnita libre es y
valor_x <- c_i / a_i
expresion <- paste0("x = ", round(valor_x, 4), ", y = t")
generar <- function(t) data.frame(x = valor_x, y = t)
}
list(expresion = expresion, generar = generar)
}
# Clasifica y resuelve un sistema lineal de dos ecuaciones y dos incógnitas
resolver_sistema <- function(A, b, tolerancia = 1e-10) {
matriz_ampliada <- cbind(A, b) # (A | B)
rango_A <- qr(A, tol = tolerancia)$rank
rango_ampliada <- qr(matriz_ampliada, tol = tolerancia)$rank
determinante <- det(A)
if (rango_A < rango_ampliada) {
tipo <- "Incompatible (sin solución)"
solucion <- NULL
} else if (rango_A == ncol(A)) {
tipo <- "Compatible determinado (solución única)"
solucion <- solve(A, b) # solo se usa si A es invertible
names(solucion) <- c("x", "y")
} else {
tipo <- "Compatible indeterminado (infinitas soluciones)"
solucion <- solucion_parametrica(A, b)
}
list(A = A, b = b, determinante = determinante,
rango_A = rango_A, rango_ampliada = rango_ampliada,
tipo = tipo, solucion = solucion)
}
# Presenta los resultados del análisis de forma legible
mostrar_sistema <- function(resultado) {
cat("Determinante de A:", round(resultado$determinante, 10), "\n")
cat("Rango de A:", resultado$rango_A,
"| Rango de (A|B):", resultado$rango_ampliada, "\n")
cat("Clasificación:", resultado$tipo, "\n")
if (is.null(resultado$solucion)) {
cat("Solución: el sistema no tiene solución\n")
} else if (is.numeric(resultado$solucion)) {
cat("Solución: x =", resultado$solucion["x"],
" y =", resultado$solucion["y"], "\n")
} else {
cat("Solución general:", resultado$solucion$expresion,
" (t es cualquier número real)\n")
}
}La función graficar_rectas() representa las dos ecuaciones del sistema. La segunda recta se dibuja con línea discontinua y más delgada, de modo que, si ambas coinciden, sigue siendo posible distinguirlas. Cuando la solución es única, se marca el punto de intersección.
graficar_rectas <- function(resultado, titulo, rango_x = c(-2, 6)) {
A <- resultado$A; b <- resultado$b
valores_x <- seq(rango_x[1], rango_x[2], length.out = 200)
# Puntos de cada recta: se despeja y cuando su coeficiente no es cero
rectas <- do.call(rbind, lapply(1:2, function(i) {
if (A[i, 2] != 0) {
data.frame(x = valores_x,
y = (b[i] - A[i, 1] * valores_x) / A[i, 2],
ecuacion = paste("Ecuación", i))
} else {
data.frame(x = b[i] / A[i, 1], y = valores_x,
ecuacion = paste("Ecuación", i))
}
}))
grafico <- ggplot(rectas, aes(x, y, colour = ecuacion,
linetype = ecuacion, linewidth = ecuacion)) +
geom_hline(yintercept = 0, colour = "grey70") +
geom_vline(xintercept = 0, colour = "grey70") +
geom_line() +
scale_colour_manual(values = c("#1f5f8b", "#c0392b")) +
scale_linetype_manual(values = c("solid", "dashed")) +
scale_linewidth_manual(values = c(2.2, 0.9)) +
labs(title = titulo, subtitle = resultado$tipo,
x = "x", y = "y", colour = NULL, linetype = NULL,
linewidth = NULL) +
theme_minimal(base_size = 11) +
theme(legend.position = "bottom")
# Punto de intersección solo si la solución es única
if (is.numeric(resultado$solucion)) {
punto <- data.frame(x = resultado$solucion["x"],
y = resultado$solucion["y"])
grafico <- grafico +
geom_point(data = punto, aes(x, y), inherit.aes = FALSE,
size = 3.5, colour = "black") +
geom_label(data = punto, inherit.aes = FALSE,
aes(x, y, label = paste0("(", x, "; ", y, ")")),
vjust = -0.6, size = 3.5)
}
grafico
}Planteamiento. Resolver el sistema
\[ \begin{cases} 2x + 3y = 12 \\ x - y = 1 \end{cases} \]
Desarrollo matemático. Este primer sistema se resuelve con los cuatro métodos para mostrar que todos conducen al mismo resultado.
Sustitución. De la segunda ecuación, \(x = 1 + y\). Al reemplazar en la primera: \(2(1 + y) + 3y = 12\), es decir, \(2 + 5y = 12\), de donde \(y = 2\) y \(x = 1 + 2 = 3\).
Igualación. Se despeja \(x\) en ambas ecuaciones: \(x = \dfrac{12 - 3y}{2}\) y \(x = 1 + y\). Al igualar, \(12 - 3y = 2 + 2y\), por lo que \(5y = 10\) y \(y = 2\); entonces \(x = 3\).
Reducción. Se multiplica la segunda ecuación por 3, \(3x - 3y = 3\), y se suma a la primera: \(5x = 15\), así que \(x = 3\); luego \(y = x - 1 = 2\).
Método matricial. La matriz de coeficientes y su determinante son
\[ A = \begin{pmatrix} 2 & 3 \\ 1 & -1 \end{pmatrix}, \qquad \det(A) = (2)(-1) - (3)(1) = -5 \neq 0. \]
Por la regla de Cramer:
\[ x = \frac{\begin{vmatrix} 12 & 3 \\ 1 & -1 \end{vmatrix}}{-5} = \frac{-12 - 3}{-5} = 3, \qquad y = \frac{\begin{vmatrix} 2 & 12 \\ 1 & 1 \end{vmatrix}}{-5} = \frac{2 - 12}{-5} = 2. \]
La comprobación en ambas ecuaciones es inmediata: \(2(3) + 3(2) = 12\) y \(3 - 2 = 1\).
Código en R. Se construyen la matriz y el vector, se aplica la función de clasificación y se verifica el resultado con la inversa de \(A\) y con el producto matricial %*%.
# Matriz de coeficientes (se llena por filas) y vector de términos independientes
A1 <- matrix(c(2, 3,
1, -1), nrow = 2, byrow = TRUE)
b1 <- c(12, 1)
sis1 <- resolver_sistema(A1, b1)
mostrar_sistema(sis1)#> Determinante de A: -5
#> Rango de A: 2 | Rango de (A|B): 2
#> Clasificación: Compatible determinado (solución única)
#> Solución: x = 3 y = 2
#> [,1] [,2]
#> [1,] 0.2 0.6
#> [2,] 0.2 -0.4
#> [,1]
#> [1,] 3
#> [2,] 2
#> [,1]
#> [1,] 12
#> [2,] 1
Resultado e interpretación. El determinante vale \(-5\) y ambos rangos son iguales a 2, por lo que el sistema es compatible determinado. R obtiene \(x = 3\), \(y = 2\) tanto con solve(A, b) como con el producto \(A^{-1}B\), y la verificación reproduce el vector \((12, 1)\). La Figura 4 muestra que las rectas se cortan en un único punto, \((3;\, 2)\).
Figura 4: Rectas del sistema compatible determinado y su punto de intersección.
Las dos rectas tienen pendientes distintas, \(-2/3\) y \(1\), lo que garantiza que se crucen exactamente una vez. Esa diferencia de pendientes es la versión geométrica de un determinante distinto de cero.
Planteamiento. Resolver el sistema
\[ \begin{cases} x + 2y = 4 \\ 2x + 4y = 10 \end{cases} \]
Desarrollo matemático. El determinante de la matriz de coeficientes es \(\det(A) = (1)(4) - (2)(2) = 0\), de modo que no existe solución única. Para saber si hay infinitas soluciones o ninguna, se aplica reducción: se multiplica la primera ecuación por 2 y se resta de la segunda.
\[ (2x + 4y) - (2x + 4y) = 10 - 8 \quad \Longrightarrow \quad 0 = 2. \]
La igualdad \(0 = 2\) es falsa, lo que indica que ningún par \((x, y)\) cumple ambas ecuaciones. En términos de rangos, las filas de \(A\) son proporcionales y \(\text{rango}(A) = 1\); en cambio, la matriz ampliada contiene la fila \((1, 2, 4)\) y la fila \((2, 4, 10)\), que no son proporcionales, por lo que \(\text{rango}(A \mid B) = 2\). Al despejar \(y\) se obtienen \(y = 2 - x/2\) e \(y = 2.5 - x/2\): dos rectas con la misma pendiente y distinta ordenada en el origen.
Código en R. Primero se muestra qué ocurre si se usa solve() sin ninguna comprobación previa; tryCatch() captura el error para que el documento continúe. Después se aplica la función de clasificación.
A2 <- matrix(c(1, 2,
2, 4), nrow = 2, byrow = TRUE)
b2 <- c(4, 10)
# solve() sin comprobación previa: falla porque A no es invertible
tryCatch(solve(A2, b2), error = function(e) conditionMessage(e))#> [1] "Lapack routine dgesv: system is exactly singular: U[2,2] = 0"
#> Determinante de A: 0
#> Rango de A: 1 | Rango de (A|B): 2
#> Clasificación: Incompatible (sin solución)
#> Solución: el sistema no tiene solución
Resultado e interpretación. solve() emite un error de matriz singular, pero no indica si el sistema carece de solución o tiene infinitas. La función propia, en cambio, encuentra \(\text{rango}(A) = 1 < \text{rango}(A \mid B) = 2\) y clasifica el sistema como incompatible, en concordancia con la contradicción \(0 = 2\). La Figura 5 confirma que las rectas son paralelas.
Figura 5: Rectas paralelas del sistema incompatible: no existe punto de intersección.
Las rectas mantienen siempre la misma distancia vertical, de 0.5 unidades, y nunca se encuentran. Por eso el sistema no tiene solución, aunque cada ecuación por separado tenga infinitas.
Planteamiento. Resolver el sistema
\[ \begin{cases} 3x - y = 2 \\ 6x - 2y = 4 \end{cases} \]
Desarrollo matemático. El determinante es \(\det(A) = (3)(-2) - (-1)(6) = -6 + 6 = 0\). Al restar de la segunda ecuación el doble de la primera se obtiene
\[ (6x - 2y) - 2(3x - y) = 4 - 2(2) \quad \Longrightarrow \quad 0 = 0. \]
La identidad \(0 = 0\) es verdadera para cualquier par \((x, y)\), lo que significa que la segunda ecuación no aporta información nueva: es la primera multiplicada por 2. En este caso \(\text{rango}(A) = \text{rango}(A \mid B) = 1\), menor que el número de incógnitas. Al despejar \(y\) en la primera ecuación, la solución general es
\[ x = t, \qquad y = 3t - 2, \qquad t \in \mathbb{R}. \]
Por ejemplo, \(t = 0\) da \((0, -2)\); \(t = 1\) da \((1, 1)\); y \(t = 2\) da \((2, 4)\). Cada uno de estos pares satisface ambas ecuaciones.
Código en R. Además de clasificar el sistema, se generan varias soluciones particulares y se verifica que todas cumplan las dos ecuaciones.
A3 <- matrix(c(3, -1,
6, -2), nrow = 2, byrow = TRUE)
b3 <- c(2, 4)
sis3 <- resolver_sistema(A3, b3)
mostrar_sistema(sis3)#> Determinante de A: 0
#> Rango de A: 1 | Rango de (A|B): 1
#> Clasificación: Compatible indeterminado (infinitas soluciones)
#> Solución general: x = t, y = 3t - 2 (t es cualquier número real)
# Soluciones particulares para distintos valores del parámetro t
particulares <- sis3$solucion$generar(t = c(-1, 0, 1, 2, 3))
# Se evalúa el lado izquierdo de cada ecuación: debe dar 2 y 4
particulares$ecuacion_1 <- 3 * particulares$x - particulares$y
particulares$ecuacion_2 <- 6 * particulares$x - 2 * particulares$y
particulares#> x y ecuacion_1 ecuacion_2
#> 1 -1 -5 2 4
#> 2 0 -2 2 4
#> 3 1 1 2 4
#> 4 2 4 2 4
#> 5 3 7 2 4
Resultado e interpretación. La función detecta rangos iguales a 1 y devuelve la solución general \(y = 3t - 2\), que coincide con la obtenida manualmente. La tabla de soluciones particulares muestra que, para cualquier valor de \(t\), la primera ecuación da 2 y la segunda da 4. La Figura 6 presenta las dos rectas superpuestas: la línea discontinua de la segunda ecuación recorre exactamente la línea gruesa de la primera, y los puntos negros corresponden a las soluciones particulares de la tabla anterior.
# Se añaden las soluciones particulares calculadas en el bloque anterior
graficar_rectas(sis3, "Ejercicio 3: rectas coincidentes", rango_x = c(-1, 3)) +
geom_point(data = particulares, aes(x, y), inherit.aes = FALSE,
size = 2.5, colour = "black")Figura 6: Rectas coincidentes del sistema compatible indeterminado: todos sus puntos son solución.
Como ambas ecuaciones describen la misma recta, cada uno de sus infinitos puntos es una solución del sistema. Este caso muestra por qué no basta con observar que \(\det(A) = 0\): el determinante nulo descarta la solución única, pero son los rangos los que distinguen entre infinitas soluciones y ninguna.
La Tabla 4 sintetiza los resultados obtenidos por la función resolver_sistema() en los tres ejercicios.
describir_solucion <- function(resultado) {
if (is.null(resultado$solucion)) return("No existe")
if (is.numeric(resultado$solucion)) {
return(paste0("x = ", resultado$solucion["x"],
"; y = ", resultado$solucion["y"]))
}
resultado$solucion$expresion
}
resumen_sistemas <- data.frame(
Sistema = c("2x + 3y = 12; x - y = 1",
"x + 2y = 4; 2x + 4y = 10",
"3x - y = 2; 6x - 2y = 4"),
"det(A)" = round(c(sis1$determinante, sis2$determinante,
sis3$determinante), 10),
"rango(A)" = c(sis1$rango_A, sis2$rango_A, sis3$rango_A),
"rango(A|B)" = c(sis1$rango_ampliada, sis2$rango_ampliada,
sis3$rango_ampliada),
"Clasificación" = c("Compatible determinado", "Incompatible",
"Compatible indeterminado"),
"Solución" = c(describir_solucion(sis1), describir_solucion(sis2),
describir_solucion(sis3)),
check.names = FALSE
)
tabla_formato(resumen_sistemas, "Resumen de los sistemas de ecuaciones resueltos")| Sistema | det(A) | rango(A) | rango(A|B) | Clasificación | Solución |
|---|---|---|---|---|---|
| 2x + 3y = 12; x - y = 1 | -5 | 2 | 2 | Compatible determinado | x = 3; y = 2 |
| x + 2y = 4; 2x + 4y = 10 | 0 | 1 | 2 | Incompatible | No existe |
| 3x - y = 2; 6x - 2y = 4 | 0 | 1 | 1 | Compatible indeterminado | x = t, y = 3t - 2 |
Planteamiento. Un terreno rectangular tiene un área de 40 m², y su largo mide 3 m más que su ancho. ¿Cuáles son sus dimensiones?
Si \(x\) representa el ancho, el largo es \(x + 3\) y la condición de área se expresa como \(x(x + 3) = 40\), es decir, \(x^2 + 3x - 40 = 0\). El discriminante es \(\Delta = 9 + 160 = 169\) y las raíces son \(x = (-3 \pm 13)/2\), esto es, \(x = 5\) o \(x = -8\).
#> Ecuación: 1 x^2 + 3 x + -40 = 0
#> Discriminante: 169
#> Tipo de solución: Dos soluciones reales diferentes
#> Raíces: 5 -8
# Solo tiene sentido físico la raíz positiva
ancho <- max(terreno$raices)
largo <- ancho + 3
cat("Ancho:", ancho, "m | Largo:", largo, "m | Área:", ancho * largo, "m2\n")#> Ancho: 5 m | Largo: 8 m | Área: 40 m2
Ambas raíces son matemáticamente correctas, pero solo \(x = 5\) tiene sentido en el contexto, porque una longitud no puede ser negativa. El terreno mide 5 m de ancho y 8 m de largo. Este ejemplo recuerda que el programa resuelve la ecuación, pero la interpretación sigue siendo responsabilidad de quien plantea el problema.
Planteamiento. Para un evento universitario se vendieron 120 entradas y se recaudaron 460 dólares. Las entradas generales costaban 5 dólares y las de estudiante, 3 dólares. ¿Cuántas se vendieron de cada tipo?
Si \(x\) es el número de entradas generales e \(y\) el de entradas de estudiante, las condiciones del problema forman el sistema \(x + y = 120\) y \(5x + 3y = 460\).
A_entradas <- matrix(c(1, 1,
5, 3), nrow = 2, byrow = TRUE)
b_entradas <- c(120, 460)
mostrar_sistema(resolver_sistema(A_entradas, b_entradas))#> Determinante de A: -2
#> Rango de A: 2 | Rango de (A|B): 2
#> Clasificación: Compatible determinado (solución única)
#> Solución: x = 50 y = 70
Se vendieron 50 entradas generales y 70 de estudiante. La comprobación es directa: \(50 + 70 = 120\) entradas y \(5(50) + 3(70) = 460\) dólares.
Una de las ventajas más claras de programar la solución es que el mismo procedimiento se aplica sin esfuerzo adicional a cualquier cantidad de casos. El bloque siguiente resuelve cinco ecuaciones almacenadas en una tabla de coeficientes; la función lapply() aplica resolver_cuadratica() a cada fila y los resultados se organizan en la Tabla 5.
coeficientes <- data.frame(a = c(1, 1, 3, 1, 2),
b = c(0, -6, 2, -1, 3),
c = c(-4, 9, 1, -6, 0))
# Se resuelve cada ecuación (cada fila de la tabla)
soluciones <- lapply(seq_len(nrow(coeficientes)), function(i) {
with(coeficientes[i, ], resolver_cuadratica(a, b, c))
})
tabla_lote <- data.frame(
a = coeficientes$a, b = coeficientes$b, c = coeficientes$c,
Discriminante = sapply(soluciones, function(s) s$discriminante),
Tipo = sapply(soluciones, function(s) s$tipo),
"Raíces" = sapply(soluciones, function(s)
paste(format(round(s$raices, 4)), collapse = " ; ")),
check.names = FALSE
)
tabla_formato(tabla_lote,
"Resolución automatizada de cinco ecuaciones de segundo grado")| a | b | c | Discriminante | Tipo | Raíces |
|---|---|---|---|---|---|
| 1 | 0 | -4 | 16 | Dos soluciones reales diferentes | 2 ; -2 |
| 1 | -6 | 9 | 0 | Una solución real doble | 3 |
| 3 | 2 | 1 | -8 | Dos soluciones complejas conjugadas | -0.3333+0.4714i ; -0.3333-0.4714i |
| 1 | -1 | -6 | 25 | Dos soluciones reales diferentes | 3 ; -2 |
| 2 | 3 | 0 | 9 | Dos soluciones reales diferentes | 0.0 ; -1.5 |
La regresión lineal simple ajusta una recta \(\hat{y} = \beta_0 + \beta_1 x\) a un conjunto de datos. Los coeficientes que minimizan la suma de los cuadrados de los residuos son la solución de un sistema de dos ecuaciones con dos incógnitas, conocido como sistema de ecuaciones normales:
\[ \begin{cases} n\,\beta_0 + \left(\sum x_i\right)\beta_1 = \sum y_i \\[4pt] \left(\sum x_i\right)\beta_0 + \left(\sum x_i^2\right)\beta_1 = \sum x_i y_i \end{cases} \]
Con un conjunto ilustrativo de seis observaciones (horas de estudio y calificación obtenida), el bloque siguiente construye ese sistema, lo resuelve con la misma función empleada en los ejercicios y compara el resultado con lm(), la función de R para modelos lineales.
horas <- 1:6
calificacion <- c(4.5, 5.8, 6.1, 7.4, 8.0, 9.2) # datos ilustrativos
n <- length(horas)
# Sistema de ecuaciones normales en forma matricial
A_normal <- matrix(c(n, sum(horas),
sum(horas), sum(horas^2)), nrow = 2, byrow = TRUE)
b_normal <- c(sum(calificacion), sum(horas * calificacion))
ajuste_sistema <- resolver_sistema(A_normal, b_normal)
mostrar_sistema(ajuste_sistema)#> Determinante de A: 105
#> Rango de A: 2 | Rango de (A|B): 2
#> Clasificación: Compatible determinado (solución única)
#> Solución: x = 3.693333 y = 0.8971429
#> (Intercept) horas
#> 3.6933333 0.8971429
En la salida de mostrar_sistema(), la primera incógnita (rotulada x) corresponde a \(\beta_0\) y la segunda (rotulada y) a \(\beta_1\). Los coeficientes obtenidos al resolver el sistema coinciden con los que calcula lm(): la ordenada en el origen es aproximadamente 3.693 y la pendiente, 0.897. En términos del ejemplo, cada hora adicional de estudio se asocia con un aumento cercano a 0.9 puntos en la calificación. La coincidencia muestra que una de las herramientas centrales de la estadística descansa, en su forma más simple, sobre la resolución de un sistema lineal de dos incógnitas.
Los ejercicios anteriores permiten resumir las ventajas de incorporar R en el estudio de estos temas:
polyroot() o lm(), lo que reduce los errores aritméticos.El desarrollo de esta publicación permitió consolidar dos ideas centrales. En las ecuaciones de segundo grado, el discriminante \(\Delta = b^2 - 4ac\) anticipa el tipo de solución antes de calcularla: dos raíces reales distintas cuando es positivo, una raíz doble cuando es nulo y un par de raíces complejas conjugadas cuando es negativo. En los sistemas lineales de dos incógnitas, la comparación entre el rango de la matriz de coeficientes y el de la matriz ampliada permite distinguir con rigor entre solución única, infinitas soluciones y ausencia de solución. En ambos temas, la interpretación gráfica, sea mediante los cortes de la parábola con el eje \(X\) o mediante la posición relativa de dos rectas, dio sentido concreto a cada resultado.
Combinar el procedimiento algebraico con la programación resultó más provechoso que emplear cualquiera de los dos por separado. La resolución manual obligó a comprender cada paso, y el código permitió verificar esos pasos, extenderlos a muchos casos y detectar situaciones que una herramienta mal empleada puede ocultar. El ejemplo más claro fue el uso de solve(): aplicada sin comprobaciones previas, esta función falla ante un sistema singular sin explicar la causa, mientras que una función construida a partir de la teoría clasifica correctamente el sistema y describe su conjunto de soluciones.
Finalmente, R demostró ser una herramienta adecuada tanto para aprender como para resolver problemas matemáticos. Sus funciones matriciales, su aritmética compleja y sus capacidades gráficas cubren todos los casos estudiados, y R Markdown convierte el trabajo en un documento reproducible y fácil de compartir. La conexión con la recta de mínimos cuadrados mostró, además, que estos contenidos no se limitan a ejercicios aislados, sino que constituyen la base de métodos estadísticos de uso cotidiano.
Baldor, A. (2007). Álgebra (2.ª ed.). Grupo Editorial Patria.
Cheng, J., y Wickham, H. (2026, 21 de julio). Migrating to Connect Cloud: Posit’s unified publishing solution. Posit. https://posit.co/blog/migrating-connect-cloud-posits-unified-publishing-solution
Grossman, S. I., y Flores Godoy, J. J. (2012). Álgebra lineal (7.ª ed.). McGraw-Hill.
R Core Team. (2024). R: A language and environment for statistical computing. R Foundation for Statistical Computing. https://www.R-project.org/
RPubs. (s.f.). Getting started with RPubs. Recuperado el 6 de noviembre de 2026, de https://rpubs.com/about/getting-started
Stewart, J., Redlin, L., y Watson, S. (2012). Precálculo: Matemáticas para el cálculo (6.ª ed.). Cengage Learning.
Wickham, H. (2016). ggplot2: Elegant graphics for data analysis (2.ª ed.). Springer-Verlag. https://ggplot2.tidyverse.org
Xie, Y. (2015). Dynamic documents with R and knitr (2.ª ed.). Chapman and Hall/CRC.
Xie, Y., Allaire, J. J., y Grolemund, G. (2018). R Markdown: The definitive guide. Chapman and Hall/CRC. https://bookdown.org/yihui/rmarkdown/