Examen de C resuelto: productos por columnas, rango y determinante exacto de una matriz entera con eliminación de Bareiss, pivoteo y long long.
Actualizado el
Código de Examen C Fundamentos de programación Nivel nota Código probado

El grupo de simulación de un laboratorio de estructuras guarda sus sistemas de ecuaciones como matrices de coeficientes enteros. Antes de lanzar un cálculo largo quieren un programa de revisión que diga si el sistema tiene solución única. Como los coeficientes son enteros, se exige un resultado exacto: no se admite ninguna operación con float ni con double.
Entrada (por la entrada estándar): dos enteros f y c (número de filas y de columnas, 1 ≤ f, c ≤ 6) seguidos de los f·c elementos de la matriz por filas, separados por espacios o saltos de línea. Cada elemento cumple |a| ≤ 20. Puede suponer que la entrada es correcta.
Salida: exactamente tres líneas. La primera empieza por productos: y contiene, separados por un espacio, los productos de los elementos de cada columna, de izquierda a derecha. La segunda tiene la forma rango: R. La tercera tiene la forma determinante: D si la matriz es cuadrada, o bien determinante: no definido si no lo es.
Debe calcular el rango y el determinante con eliminación gaussiana sin fracciones (método de Bareiss). En el paso k cada elemento por debajo y a la derecha del pivote se sustituye por (a[i][j]·a[k][k] − a[i][k]·a[k][j]) / p, donde p es el pivote del paso anterior (vale 1 en el primer paso). Esa división siempre es exacta. Si el pivote vale cero, se busca más abajo una fila con un elemento no nulo en esa columna y se intercambian las dos filas. Cada intercambio cambia el signo del determinante. Si toda la columna es nula, se pasa a la siguiente columna sin avanzar de fila.
Organice el código en funciones: una que lea la matriz, una que calcule los productos por columnas y una función bareiss que triangule la matriz, devuelva el rango e informe del signo acumulado. Justifique qué tipo entero elige: los valores intermedios y el determinante pueden superar el rango de int.
Entrada de ejemplo
3 3
2 -1 0
-1 2 -1
0 -1 2Salida esperada
productos: 0 2 0
rango: 3
determinante: 4- Lectura correcta de la matriz y cálculo de los productos por columnas antes de modificar la matriz
- 0,5
- Búsqueda de pivote no nulo, intercambio de filas y control del signo del determinante
- 0,75
- Eliminación de Bareiss con división exacta entre el pivote anterior, sin decimales y sin desbordamiento
- 0,75
- Rango en matrices rectangulares o singulares y determinante solo cuando la matriz es cuadrada
- 0,5
Pistas
Pista 1 · ¿En qué orden conviene hacer las cosas?
La eliminación destruye la matriz original. Por eso los productos por columnas hay que calcularlos antes de llamar a bareiss, o sobre una copia. El rango cuenta cuántos pivotes no nulos has encontrado. No hay una fórmula aparte: es el contador de filas r que avanza cada vez que fijas un pivote.
Pista 2 · ¿Qué hago con un pivote nulo?
Busca con un while la primera fila p ≥ r cuyo elemento en la columna actual sea distinto de cero. Si no existe (p == f), esa columna no aporta pivote: haz continue sin incrementar r. Si existe y es otra fila, intercambia las filas completas y multiplica el signo por −1.
Pista 3 · ¿Por qué no pierdo precisión al dividir?
Tras cada paso, los elementos de la zona sin eliminar son menores (determinantes de submatrices) de la matriz original. El numerador de la fórmula es múltiplo exacto del pivote anterior. Si guardas en prev el pivote recién usado y divides entre él en el paso siguiente, todos los valores siguen siendo enteros. Al acabar, en una matriz cuadrada de rango completo, el último pivote es el determinante salvo el signo.
Solución
Solución explicada
La solución separa las tres tareas en funciones con un contrato claro. leer_matriz rellena una matriz fija de 6×6 y devuelve las dimensiones mediante punteros, como se explica en el ejercicio de paso por valor y por referencia. productos_columnas recorre la matriz por columnas. bareiss la triangula en el sitio y devuelve el rango.
Todos los elementos se guardan en long long, aunque la entrada quepa en un int. Con |a| ≤ 20 y tamaño 6, los productos intermedios llegan a valores del orden de 1016. Eso cabe holgadamente en 64 bits, pero no en 32.
#include <stdio.h>
#define MAXD 6
/* Lee f, c y la matriz por filas. Devuelve 1 si todo es correcto. */
static int leer_matriz(long long a[MAXD][MAXD], int *f, int *c)
{
if (scanf("%d %d", f, c) != 2) return 0;
if (*f < 1 || *f > MAXD || *c < 1 || *c > MAXD) return 0;
for (int i = 0; i < *f; i++)
for (int j = 0; j < *c; j++)
if (scanf("%lld", &a[i][j]) != 1) return 0;
return 1;
}
/* prod[j] = producto de los elementos de la columna j. */
static void productos_columnas(long long a[MAXD][MAXD], int f, int c,
long long prod[MAXD])
{
for (int j = 0; j < c; j++) {
prod[j] = 1;
for (int i = 0; i < f; i++)
prod[j] *= a[i][j];
}
}
/* Eliminacion sin fracciones de Bareiss. Modifica a, devuelve el rango
y deja en *signo (+1 o -1) el efecto de los intercambios de filas. */
static int bareiss(long long a[MAXD][MAXD], int f, int c, int *signo)
{
int r = 0;
long long prev = 1;
*signo = 1;
for (int col = 0; col < c && r < f; col++) {
int p = r;
while (p < f && a[p][col] == 0)
p++;
if (p == f)
continue; /* columna sin pivote */
if (p != r) {
for (int j = 0; j < c; j++) {
long long t = a[p][j];
a[p][j] = a[r][j];
a[r][j] = t;
}
*signo = -*signo;
}
for (int i = r + 1; i < f; i++) {
for (int j = col + 1; j < c; j++)
a[i][j] = (a[i][j] * a[r][col] - a[i][col] * a[r][j]) / prev;
a[i][col] = 0;
}
prev = a[r][col];
r++;
}
return r;
}
int main(void)
{
long long a[MAXD][MAXD];
long long prod[MAXD];
int f, c, signo;
if (!leer_matriz(a, &f, &c)) {
printf("entrada no valida\n");
return 0;
}
productos_columnas(a, f, c, prod);
printf("productos:");
for (int j = 0; j < c; j++)
printf(" %lld", prod[j]);
printf("\n");
int rango = bareiss(a, f, c, &signo);
printf("rango: %d\n", rango);
if (f != c)
printf("determinante: no definido\n");
else if (rango < f)
printf("determinante: 0\n");
else
printf("determinante: %lld\n", signo * a[f - 1][f - 1]);
return 0;
}main lee la matriz y llama primero a productos_columnas. Ese orden es obligatorio, porque bareiss sobrescribe a. Cada producto se inicializa a 1 y se multiplica fila a fila. El mayor valor posible en módulo es 206 = 64 000 000. En el caso de prueba de 6×6 cada columna tiene un −20 y cinco 20, y el programa imprime seis veces -64000000.
En bareiss la variable r es la fila donde colocaremos el próximo pivote, y col recorre columnas. El bucle termina cuando se acaban las columnas o cuando ya hay un pivote en cada fila. El while busca la primera fila p con elemento no nulo. Si no la encuentra, continue salta de columna sin tocar r. Esto es lo que permite tratar matrices rectangulares y singulares.
Si el pivote está en otra fila, se intercambian las filas completas y se invierte *signo. Luego, para cada fila inferior i y cada columna j a la derecha, se aplica la fórmula del enunciado dividiendo entre prev. Después se anula a[i][col], que no interviene en el cálculo. La anulación va después del bucle en j, porque ese bucle todavía necesita el valor antiguo de a[i][col].
En el ejemplo, con la matriz tridiagonal 3×3, el primer pivote es 2 y prev vale 1. La celda (1,1) pasa a 2·2 − (−1)(−1) = 3 y la celda (2,2) pasa a 2·2 − 0·0 = 4. En el segundo paso, con pivote 3 y prev igual a 2, la celda (2,2) queda (4·3 − (−2)(−2)) / 2 = 4. Ese último pivote es el determinante. El rango es 3.
El segundo test empieza con un cero en la esquina. El programa intercambia las dos primeras filas y el signo pasa a −1. Después la tercera columna no tiene pivote en la fila 2, así que el rango se queda en 2 y el determinante es 0: la tercera fila menos el doble de la segunda reproduce la primera. En la matriz 2×4 solo aparece un pivote, y la salida dice no definido.
Para el determinante, main distingue tres casos. Si la matriz no es cuadrada, el determinante no está definido. Si el rango es menor que f, el determinante es 0. En otro caso es signo * a[f-1][f-1]. El test 6×6 lo confirma con -8192000000, que no cabe en un int de 32 bits. La matriz 1×1 y la matriz nula comprueban los bordes.
Traza paso a paso · generada ejecutando el código
| Paso | r | col | i | j | prev | a_ij | Celda |
|---|---|---|---|---|---|---|---|
| 1 | 0 | 0 | 0 | 0 | 1 | 2 | (0,0) |
| 2 | 0 | 0 | 1 | 1 | 1 | 3 | (1,1) |
| 3 | 0 | 0 | 1 | 2 | 1 | -2 | (1,2) |
| 4 | 0 | 0 | 2 | 1 | 1 | -2 | (2,1) |
| 5 | 0 | 0 | 2 | 2 | 1 | 4 | (2,2) |
| 6 | 1 | 1 | 1 | 1 | 2 | 3 | (1,1) |
| 7 | 1 | 1 | 2 | 2 | 2 | 4 | (2,2) |
| 8 | 2 | 2 | 2 | 2 | 3 | 4 | (2,2) |
Casos de prueba
| Caso | Entrada | Salida esperada | Salida real | Resultado |
|---|---|---|---|---|
| Ejemplo del enunciado: tridiagonal 3x3 | 3 3
2 -1 0
-1 2 -1
0 -1 2 | productos: 0 2 0
rango: 3
determinante: 4 | productos: 0 2 0
rango: 3
determinante: 4 | OK |
| Pivote nulo con intercambio y matriz singular | 3 3
0 2 1
3 1 4
6 4 9 | productos: 0 8 36
rango: 2
determinante: 0 | productos: 0 8 36
rango: 2
determinante: 0 | OK |
| Matriz rectangular 2x4 de rango 1 | 2 4
1 2 3 4
2 4 6 8 | productos: 2 8 18 32
rango: 1
determinante: no definido | productos: 2 8 18 32
rango: 1
determinante: no definido | OK |
| Un solo elemento negativo | 1 1
-7 | productos: -7
rango: 1
determinante: -7 | productos: -7
rango: 1
determinante: -7 | OK |
| Matriz nula 2x2 | 2 2
0 0
0 0 | productos: 0 0
rango: 0
determinante: 0 | productos: 0 0
rango: 0
determinante: 0 | OK |
| Límite 6x6 con valores ±20 | 6 6
-20 20 20 20 20 20
20 -20 20 20 20 20
20 20 -20 20 20 20
20 20 20 -20 20 20
20 20 20 20 -20 20
20 20 20 20 20 -20 | productos: -64000000 -64000000 -64000000 -64000000 -64000000 -64000000
rango: 6
determinante: -8192000000 | productos: -64000000 -64000000 -64000000 -64000000 -64000000 -64000000
rango: 6
determinante: -8192000000 | OK |
Salidas reales: código compilado con gcc 14.4.0 (C17) y ejecutado en un contenedor aislado el 23 de octubre de 2026.
Complejidad
Llamemos k = min(f, c). La eliminación hace, para cada uno de los como mucho k pivotes, un recorrido por las filas inferiores y las columnas de la derecha. Eso da O(f·c·k) operaciones; para una matriz cuadrada de orden n, O(n³). La búsqueda de pivote y el intercambio cuestan O(f + c) por paso, que es menos. Los productos por columnas son O(f·c).
La memoria es constante: una matriz fija de 6×6 long long, el vector de productos y unas pocas variables escalares. No se usa memoria dinámica. El límite de tamaño también controla el desbordamiento. Los numeradores de Bareiss están acotados por el producto de dos menores, y la desigualdad de Hadamard los mantiene por debajo de 1017 con estos datos. Con matrices mayores habría que razonar de nuevo los límites.
Errores típicos
- Calcular los productos por columnas después de llamar a
bareiss. Para entonces la matriz ya está triangulada y los productos salen casi siempre 0. - Guardar la matriz en
int. El productoa[i][j] * a[r][col]desborda antes de la división, y el determinante del caso 6×6 ni siquiera cabe en 32 bits. - Olvidar invertir el signo al intercambiar filas. El rango sale bien, pero el determinante sale con el signo cambiado en cuanto hay un número impar de intercambios.
- Anular
a[i][col]antes de terminar el bucle enj. Los elementos de esa fila se calculan entonces con un cero en lugar del multiplicador real. - No saltar las columnas nulas e incrementar
rde todos modos. El pivote siguiente puede ser 0, y en el paso posterior se divide entre cero. - Usar eliminación con
doubley redondear al final. Pueden aparecer resultados como-0.000000o valores con error de redondeo, que el enunciado prohíbe expresamente.
Variantes
Resolver el sistema cuando el determinante no es cero
Si se añade una columna de términos independientes (matriz f×(f+1)), basta llamar a bareiss sobre la matriz ampliada sin modificarla. El sistema es compatible determinado si el rango de la parte cuadrada vale f. Para obtener cada incógnita como fracción exacta x_i = det_i / det se aplica la regla de Cramer: se sustituye la columna i por los términos independientes y se reutiliza la misma función. Al final se simplifica cada fracción dividiendo numerador y denominador por su máximo común divisor.
Suma de las cifras del determinante
Una pregunta corta clásica pide, además, cuántas cifras tiene |det| y cuánto suman. Se resuelve con un bucle while (n > 0) { suma += n % 10; n /= 10; cifras++; } sobre un long long positivo. El caso det == 0 hay que tratarlo aparte: tiene una cifra, no cero. Para repasar recorridos básicos de matrices puede ver el ejercicio de sumas, traspuesta y traza en C.
En los temarios
Las matrices como arrays bidimensionales y su relación con los punteros figuran en el programa de Algoritmos y Programación I (95.11) de la Universidad de Buenos Aires. Su unidad III trata arrays, arrays multidimensionales, arrays y punteros e inicialización de arrays. Este ejercicio trabaja justo esos contenidos: una matriz fija pasada a funciones y modificada en el sitio.
En un nivel más orientado a sistemas, el programa de CC3301 Programación de Software de Sistemas de la Universidad de Chile incluye la programación en C sobre Linux. Allí el control de tipos enteros y de desbordamientos que exige este ejercicio forma parte del trabajo cotidiano.
Historial de cambios
- Reescrito con la plantilla de Código de Examen: enunciado de examen, pistas, solución explicada, casos de prueba y código compilado y ejecutado; versión en inglés enlazada.
- Publicación original.
Artículo generado mediante AI.larebelion



Comentarios
Publicar un comentario