// Helios-Tech -- Cadenas de Markov para Prediccion Climatica
// TB1 | Matematica Discreta | Grupo 07 | NRC: 15344
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <cmath>
using namespace std;
// SECCION 1 -- CONSTANTES
const int NUM_ESTADOS = 4;
const int DIAS_HISTORIAL = 60;
const int DIAS_PREDICCION = 4;
const double TOLERANCIA = 1e-9;
const int MAX_ITER_ESTACIONARIA = 10000;
const string NOMBRES[NUM_ESTADOS] = {
"Soleado", "Parcialmente Soleado", "Parcialmente Nublado", "Nublado"
};
const string ABREV[NUM_ESTADOS] = { " S ", " PS ", " PN ", " N " };
const double EFICIENCIA[NUM_ESTADOS] = { 95.0, 70.0, 40.0, 10.0 };
// SECCION 2 -- INTERFAZ
void mostrarMenu() {
cout << "\n [1] Ingresar historial (60 dias)\n";
cout << " [2] Mostrar historial\n";
cout << " [3] Matriz de Transicion\n";
cout << " [4] Predecir clima (n+1 a n+4)\n";
cout << " [5] Recomendacion energetica\n";
cout << " [6] Distribucion Estacionaria\n";
cout << " [7] Salir\n";
cout << " Opcion: ";
void mostrarEstados() {
cout << "\n Estados: ";
for (int i = 0; i < NUM_ESTADOS; i++)
cout << "[" << i << "] " << NOMBRES[i] << " ";
cout << "\n\n";
bool validarEstado(int e) { return (e >= 0 && e < NUM_ESTADOS); }
// SECCION 3 -- HISTORIAL
vector<int> ingresarHistorial() {
vector<int> historial;
[Link](DIAS_HISTORIAL);
mostrarEstados();
cout << " Ingrese el estado [0-3] para cada dia:\n\n";
for (int dia = 1; dia <= DIAS_HISTORIAL; dia++) {
int estado = -1;
bool valido = false;
while (!valido) {
cout << " Dia " << setw(2) << dia << " -> ";
cin >> estado;
if ([Link]() || !validarEstado(estado)) {
[Link](); [Link](1000, '\n');
cout << " [!] Valor invalido. Ingrese 0, 1, 2 o 3.\n";
else { valido = true; }
historial.push_back(estado);
cout << " " << NOMBRES[estado] << "\n";
cout << "\n [OK] Historial de " << DIAS_HISTORIAL << " dias registrado.\n";
return historial;
void mostrarHistorial(const vector<int>& h) {
if ([Link]()) { cout << "\n [!] Sin historial aun.\n"; return; }
cout << "\n Dia Estado\n " << string(34, '-') << "\n";
for (int i = 0; i < (int)[Link](); i++)
cout << " " << setw(3) << (i + 1) << " " << NOMBRES[h[i]] << "\n";
// SECCION 4 -- MATRIZ DE TRANSICION
// p_ij = n_ij / n_i* (Anderson & Goodman, 1957, ec. 2.8)
vector<vector<int>> construirConteos(const vector<int>& h) {
vector<vector<int>> c(NUM_ESTADOS, vector<int>(NUM_ESTADOS, 0));
for (int i = 0; i < (int)[Link]() - 1; i++)
c[h[i]][h[i + 1]]++;
return c;
vector<vector<double>> construirMatrizTransicion(const vector<vector<int>>& c) {
vector<vector<double>> P(NUM_ESTADOS, vector<double>(NUM_ESTADOS, 0.0));
for (int i = 0; i < NUM_ESTADOS; i++) {
int total = 0;
for (int j = 0; j < NUM_ESTADOS; j++) total += c[i][j];
for (int j = 0; j < NUM_ESTADOS; j++)
P[i][j] = (total == 0) ? 1.0 / NUM_ESTADOS : (double)c[i][j] / total;
return P;
void mostrarMatrizTransicion(const vector<vector<double>>& P) {
cout << "\n Matriz de Transicion P (Anderson & Goodman, 1957)\n\n";
cout << " " << setw(24) << " ";
for (int j = 0; j < NUM_ESTADOS; j++) cout << setw(9) << ABREV[j];
cout << "\n " << string(62, '-') << "\n";
for (int i = 0; i < NUM_ESTADOS; i++) {
cout << " " << left << setw(24) << NOMBRES[i] << right;
double suma = 0.0;
for (int j = 0; j < NUM_ESTADOS; j++) {
cout << setw(9) << fixed << setprecision(4) << P[i][j];
suma += P[i][j];
cout << " (suma=" << fixed << setprecision(2) << suma << ")\n";
}
// SECCION 5 -- PREDICCION CLIMATICA
// pi_k = pi_0 x P^k (Norris, 1997; Weisstein, 2026)
vector<double> multiplicarVectorMatriz(const vector<double>& v,
const vector<vector<double>>& P) {
vector<double> r(NUM_ESTADOS, 0.0);
for (int j = 0; j < NUM_ESTADOS; j++)
for (int i = 0; i < NUM_ESTADOS; i++)
r[j] += v[i] * P[i][j];
return r;
vector<double> construirVectorInicial() {
mostrarEstados();
cout << " Estado del dia actual [0-3]: ";
int e = -1;
while (!validarEstado(e)) {
cin >> e;
if ([Link]() || !validarEstado(e)) {
[Link](); [Link](1000, '\n');
cout << " [!] Invalido. Intente de nuevo: ";
vector<double> pi0(NUM_ESTADOS, 0.0);
pi0[e] = 1.0;
cout << " Estado: " << NOMBRES[e] << "\n";
return pi0;
void predecirClima(const vector<vector<double>>& P) {
cout << "\n === PREDICCION CLIMATICA (pi_k = pi_0 x P^k) ===\n";
vector<double> pi = construirVectorInicial();
for (int k = 1; k <= DIAS_PREDICCION; k++) {
pi = multiplicarVectorMatriz(pi, P);
int mejor = 0;
for (int i = 1; i < NUM_ESTADOS; i++)
if (pi[i] > pi[mejor]) mejor = i;
cout << "\n Dia n+" << k << ":\n";
for (int i = 0; i < NUM_ESTADOS; i++) {
cout << " " << left << setw(24) << NOMBRES[i]
<< right << setw(7) << fixed << setprecision(2)
<< pi[i] * 100 << "%";
if (i == mejor) cout << " << mas probable";
cout << "\n";
// SECCION 6 -- RECOMENDACION ENERGETICA
// E[efic] = sum_i( pi_i * eficiencia_i )
void recomendacionEnergetica(const vector<vector<double>>& P) {
cout << "\n === RECOMENDACION ENERGETICA HELIOS-TECH ===\n";
vector<double> pi = construirVectorInicial();
cout << "\n";
for (int k = 1; k <= DIAS_PREDICCION; k++) {
pi = multiplicarVectorMatriz(pi, P);
double efic = 0.0;
for (int i = 0; i < NUM_ESTADOS; i++) efic += pi[i] * EFICIENCIA[i];
cout << " Dia n+" << k << " | Eficiencia: "
<< fixed << setprecision(1) << efic << "% -> ";
if (efic >= 75.0) cout << "ALTA GENERACION: descargar baterias.\n";
else if (efic >= 50.0) cout << "MODERADA: mantener carga parcial.\n";
else if (efic >= 25.0) cout << "BAJA: cargar baterias preventivamente.\n";
else cout << "CRITICO: activar generadores de respaldo.\n";
// SECCION 7 -- DISTRIBUCION ESTACIONARIA
//
// La distribucion estacionaria pi* satisface:
// pi* = pi* x P y sum(pi*_i) = 1
//
// Esto equivale al sistema lineal (Norris, 1997):
// pi*(P - I) = 0 sujeto a sum(pi*_i) = 1
//
// Metodo: Se reemplaza la ultima ecuacion de (P^T - I)*x = 0
// por la condicion de normalizacion sum(x_i) = 1,
// y se resuelve con eliminacion gaussiana.
// Resuelve Ax = b por eliminacion gaussiana con pivoteo parcial
// Retorna el vector solucion, o vector vacio si el sistema es singular
vector<double> gaussianElimination(vector<vector<double>> A, vector<double> b) {
int n = (int)[Link]();
for (int col = 0; col < n; col++) {
// Pivoteo parcial
int pivotRow = col;
for (int row = col + 1; row < n; row++)
if (fabs(A[row][col]) > fabs(A[pivotRow][col]))
pivotRow = row;
swap(A[col], A[pivotRow]);
swap(b[col], b[pivotRow]);
if (fabs(A[col][col]) < TOLERANCIA) return {}; // singular
for (int row = col + 1; row < n; row++) {
double factor = A[row][col] / A[col][col];
for (int k = col; k < n; k++)
A[row][k] -= factor * A[col][k];
b[row] -= factor * b[col];
// Sustitucion hacia atras
vector<double> x(n, 0.0);
for (int i = n - 1; i >= 0; i--) {
x[i] = b[i];
for (int j = i + 1; j < n; j++)
x[i] -= A[i][j] * x[j];
x[i] /= A[i][i];
return x;
void calcularDistribucionEstacionaria(const vector<vector<double>>& P) {
cout << "\n === DISTRIBUCION ESTACIONARIA (LARGO PLAZO) ===\n";
cout << "\n Resolviendo sistema: pi* = pi* x P, sum(pi*_i) = 1\n";
cout << " Metodo: Eliminacion Gaussiana (Norris, 1997)\n\n";
// Construir sistema (P^T - I) * x = 0, luego reemplazar ultima fila
// por condicion de normalizacion: x_0 + x_1 + ... + x_{n-1} = 1
int n = NUM_ESTADOS;
vector<vector<double>> A(n, vector<double>(n, 0.0));
vector<double> b(n, 0.0);
// Filas 0 .. n-2: columna j de (P^T - I), es decir P[j][i] - delta(i,j)
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n; j++)
A[i][j] = P[j][i] - (i == j ? 1.0 : 0.0);
b[i] = 0.0;
// Ultima fila: condicion de normalizacion
for (int j = 0; j < n; j++) A[n - 1][j] = 1.0;
b[n - 1] = 1.0;
vector<double> pi = gaussianElimination(A, b);
if ([Link]()) {
// Fallback: metodo iterativo (potencia) si el sistema es singular
cout << " [!] Sistema singular. Usando metodo iterativo...\n\n";
[Link](n, 1.0 / n);
for (int iter = 0; iter < MAX_ITER_ESTACIONARIA; iter++) {
vector<double> piNuevo = multiplicarVectorMatriz(pi, P);
double diff = 0.0;
for (int i = 0; i < n; i++) diff += fabs(piNuevo[i] - pi[i]);
pi = piNuevo;
if (diff < TOLERANCIA) break;
// Verificacion: pi* x P debe ser igual a pi*
vector<double> verificacion = multiplicarVectorMatriz(pi, P);
double errorMax = 0.0;
for (int i = 0; i < n; i++)
errorMax = max(errorMax, fabs(verificacion[i] - pi[i]));
// Mostrar resultados
cout << " Estado climático pi* (%) Eficiencia ponderada\n";
cout << " " << string(58, '-') << "\n";
double eficLP = 0.0;
for (int i = 0; i < n; i++) {
eficLP += pi[i] * EFICIENCIA[i];
cout << " " << left << setw(26) << NOMBRES[i]
<< right << setw(8) << fixed << setprecision(2) << pi[i] * 100 << "%"
<< " " << setw(5) << fixed << setprecision(1)
<< pi[i] * EFICIENCIA[i] << "%\n";
}
cout << " " << string(58, '-') << "\n";
cout << " " << left << setw(26) << "TOTAL (eficiencia media)"
<< right << setw(8) << "100.00%"
<< " " << setw(5) << fixed << setprecision(1) << eficLP << "%\n";
cout << "\n Error de verificacion (|pi*P - pi*|_max): "
<< scientific << setprecision(2) << errorMax << "\n";
cout << "\n Interpretacion:\n";
cout << " A largo plazo, el sistema converge a esta distribucion\n";
cout << " independientemente del estado inicial (Weisstein, 2026).\n";
cout << " La eficiencia media de largo plazo es "
<< fixed << setprecision(1) << eficLP << "%.\n";
if (eficLP >= 75.0)
cout << " -> Operacion sostenida en ALTA GENERACION.\n";
else if (eficLP >= 50.0)
cout << " -> Operacion sostenida en nivel MODERADO.\n";
else if (eficLP >= 25.0)
cout << " -> Operacion sostenida en nivel BAJO: planificar respaldo.\n";
else
cout << " -> Operacion sostenida en nivel CRITICO: requiere generadores.\n";
// SECCION 8 -- MAIN
int main() {
vector<int> historial;
vector<vector<int>> conteos;
vector<vector<double>> matrizP;
bool cargado = false;
int opcion = 0;
cout << "\n Helios-Tech | Prediccion Climatica | Cadenas de Markov\n";
do {
mostrarMenu();
cin >> opcion;
if ((opcion >= 2 && opcion <= 6) && !cargado) {
cout << "\n [!] Primero ingrese el historial (opcion 1).\n";
continue;
switch (opcion) {
case 1:
historial = ingresarHistorial();
conteos = construirConteos(historial);
matrizP = construirMatrizTransicion(conteos);
cargado = true;
break;
case 2: mostrarHistorial(historial); break;
case 3: mostrarMatrizTransicion(matrizP); break;
case 4: predecirClima(matrizP); break;
case 5: recomendacionEnergetica(matrizP); break;
case 6: calcularDistribucionEstacionaria(matrizP); break;
case 7: cout << "\n Hasta pronto\n\n"; break;
default: cout << "\n [!] Opcion invalida.\n";
}
} while (opcion != 7);
return 0;