Una forma sencilla y rápida de aprender JAVA, observando y deduciendo cómo se comporta el lenguaje a través de ejemplos prácticos.
Archivo del blog
sábado, 1 de febrero de 2025
Problema del viajante de Comercio TSP (IX.2). Método Genético (GA).
Funcionamiento:
> Configuración Inicial:
Genera una matriz de distancias (simétrica/asimétrica) entre ciudades.
Crea una población inicial de rutas aleatorias.
> Evolución de Soluciones:
Selección: Usa torneos para elegir las mejores rutas como padres.
Cruce (Order Crossover): Combina segmentos de rutas para generar descendencia.
Mutación Adaptativa: Aplica swap o inversión de segmentos para diversificar rutas.
Élitismo: Conserva las mejores soluciones entre generaciones.
> Optimización Dinámica:
Ajusta automáticamente la tasa de mutación si no hay mejoras recientes.
Permite límite de tiempo (ej: 30 segundos) para equilibrar calidad y eficiencia.
Características Destacadas:
✅ Matrices Personalizables: Genera matrices aleatorias con rangos de distancias definidos.
✅ Parámetros Adaptativos: Mutación variable y ajuste de población para evitar estancamientos.
✅ Informes Ejecutivos: Guarda resultados detallados (tiempo, mejor ruta, parámetros).
✅ Interactividad: Menú intuitivo para configurar y ejecutar el algoritmo.
Código Java (TSPGeneticAlgorithm.java):
package tspgeneticalgorithm;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class TSPGeneticAlgorithm {
private final int[][] distanceMatrix;
private final int numCities;
private int populationSize = 100; // Valor optimizado
private int numGenerations = 1500; // Valor irrelevante (lo controla el tiempo)
private double mutationRate = 0.04; // Tasa base (configurable por el usuario)
private double currentMutationRate; // Tasa usada durante la ejecución
private final Random random = new Random();
private long timeLimitMillis = 10000; // 10 segundos
private String matrixTypeDescription = "";
private int minDistanceValue = 1;
private int maxDistanceValue = 1000;
private boolean includeMatrixInReport = false;
private int generation = 0;
private int lastImprovement = 0;
// Operadores genéticos
private final CrossoverOperator crossoverOperator;
private final MutationOperator mutationOperator = new SwapMutation();
public TSPGeneticAlgorithm(int[][] distanceMatrix) {
validateMatrix(distanceMatrix);
this.distanceMatrix = distanceMatrix;
this.numCities = distanceMatrix.length;
this.crossoverOperator = new OrderCrossover(this.distanceMatrix);
this.currentMutationRate = this.mutationRate;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in).useLocale(Locale.US);
TSPGeneticAlgorithm ga = null;
int[][] distanceMatrix = null;
String matrixTypeDescription = "";
int minDistanceValue = 1;
int maxDistanceValue = 1000;
boolean includeMatrixInReport = false;
int defaultNumNodes = 16;
while (true) {
System.out.println("\n--- Menú Principal ---");
System.out.println("1. Generar matriz de distancias");
System.out.println("2. Configurar parámetros del AG");
System.out.println("3. Mostrar parámetros actuales");
System.out.println("4. Ejecutar algoritmo genético");
System.out.println("0. Salir");
System.out.print("Seleccione una opción [0-4]: ");
int choice;
try {
choice = scanner.nextInt();
scanner.nextLine();
} catch (InputMismatchException e) {
System.out.println("Entrada inválida. Por favor, ingrese un número.");
scanner.next();
continue;
}
switch (choice) {
case 1 -> {
Object[] matrixInfo = configureAndGenerateMatrix(scanner, minDistanceValue, maxDistanceValue, defaultNumNodes);
distanceMatrix = (int[][]) matrixInfo[0];
matrixTypeDescription = (String) matrixInfo[1];
minDistanceValue = (Integer) matrixInfo[2];
maxDistanceValue = (Integer) matrixInfo[3];
includeMatrixInReport = (Boolean) matrixInfo[4];
if (distanceMatrix != null) {
ga = new TSPGeneticAlgorithm(distanceMatrix);
ga.setMatrixGenerationInfo(matrixTypeDescription, minDistanceValue, maxDistanceValue);
ga.setIncludeMatrixInReport(includeMatrixInReport);
}
}
case 2 -> {
if (distanceMatrix != null) {
if (ga == null) {
ga = new TSPGeneticAlgorithm(distanceMatrix);
}
configureGAParameters(scanner, ga);
} else {
System.out.println("Por favor, genere la matriz de distancias primero.");
}
}
case 3 -> {
if (ga != null) {
System.out.println("\n--- MATRIZ DE DISTANCIAS ---");
printDistanceMatrix(ga.distanceMatrix, ga.maxDistanceValue);
System.out.println("\nNodos: " + ga.numCities);
System.out.println("Rango distancias: [" + ga.minDistanceValue + " - " + ga.maxDistanceValue + "]");
System.out.println("Tipo de matriz: " + ga.matrixTypeDescription);
System.out.println("\n--- PARÁMETROS (AG) ---");
ga.showCurrentParametersAG();
} else {
System.out.println("Por favor, genere la matriz de distancias primero.");
}
}
case 4 -> {
if (ga != null) {
long defaultTimeLimit = 10;
System.out.printf("Tiempo límite en segundos (0=ilimitado) [%d]: ", defaultTimeLimit);
String input = scanner.nextLine().trim();
try {
long timeLimitSeconds = input.isEmpty() ? defaultTimeLimit : Long.parseLong(input);
ga.timeLimitMillis = (timeLimitSeconds > 0) ? TimeUnit.SECONDS.toMillis(timeLimitSeconds) : -1;
} catch (NumberFormatException e) {
System.out.println("Entrada inválida. Usando 10 segundos por defecto.");
ga.timeLimitMillis = TimeUnit.SECONDS.toMillis(defaultTimeLimit);
}
runGeneticAlgorithm(ga);
} else {
System.out.println("Por favor, genere la matriz de distancias primero.");
}
}
case 0 -> {
System.out.println("Saliendo del programa.");
scanner.close();
return;
}
default ->
System.out.println("Opción no válida. Intente de nuevo.");
}
}
}
private static Object[] configureAndGenerateMatrix(Scanner scanner, int defaultMinDistance, int defaultMaxDistance, int defaultNumNodes) {
System.out.println("\n--- Configuración de la Matriz ---");
// Configurar número de nodos
int numNodes = 0;
while (numNodes <= 3) {
System.out.printf("Número de nodos [%d]: ", defaultNumNodes);
String numNodesStr = scanner.nextLine();
if (numNodesStr.isEmpty()) {
numNodes = defaultNumNodes;
if (numNodes <= 3) {
System.out.println("El valor por defecto debe ser mayor que 3. Cambie el valor por defecto.");
continue;
}
break;
}
try {
numNodes = Integer.parseInt(numNodesStr);
if (numNodes <= 3) {
System.out.println("El número de nodos debe ser mayor que 3.");
}
} catch (NumberFormatException e) {
System.out.println("Entrada inválida. Por favor, ingrese un número.");
}
}
// Configurar tipo de matriz
System.out.println("Tipo de matriz:");
System.out.println("1. Aleatoria Simétrica");
System.out.println("2. Aleatoria Asimétrica");
System.out.print("Seleccione el tipo de matriz [1]: ");
int matrixType = 0;
while (matrixType != 1 && matrixType != 2) {
String matrixTypeStr = scanner.nextLine();
if (matrixTypeStr.isEmpty()) {
matrixType = 1;
break;
}
try {
matrixType = Integer.parseInt(matrixTypeStr);
if (matrixType != 1 && matrixType != 2) {
System.out.println("Tipo de matriz no válido. Elija 1 o 2.");
}
} catch (NumberFormatException e) {
System.out.println("Entrada inválida. Por favor, ingrese 1 o 2.");
}
}
// Configurar valor mínimo
int minDistance = 0;
while (true) {
System.out.printf("Valor mínimo de la distancia [%d]: ", defaultMinDistance);
String minDistanceStr = scanner.nextLine();
if (minDistanceStr.isEmpty()) {
minDistance = defaultMinDistance;
break;
}
try {
minDistance = Integer.parseInt(minDistanceStr);
break;
} catch (NumberFormatException e) {
System.out.println("Entrada inválida. Por favor, ingrese un número.");
}
}
// Configurar valor máximo (validación reforzada)
int maxDistance = 0;
while (true) {
System.out.printf("Valor máximo de la distancia [%d]: ", defaultMaxDistance);
String maxDistanceStr = scanner.nextLine();
try {
if (maxDistanceStr.isEmpty()) {
maxDistance = defaultMaxDistance;
} else {
maxDistance = Integer.parseInt(maxDistanceStr);
}
// Validación crítica
if (maxDistance < minDistance) {
System.out.println("ERROR: El máximo debe ser >= al mínimo (" + minDistance + ")");
System.out.println("Por favor, ingrese un valor válido.");
continue;
}
break; // Salir solo si el valor es válido
} catch (NumberFormatException e) {
System.out.println("Entrada inválida. Ingrese un número.");
}
}
// Configurar inclusión en informe
System.out.print("¿Incluir la matriz en el informe de ejecución? [S/n]: ");
String includeMatrixOption = scanner.nextLine();
boolean includeMatrixInReport = !includeMatrixOption.equalsIgnoreCase("n"); // Enter = Sí
// Generar matriz
int[][] distanceMatrix;
String matrixTypeDescription;
if (matrixType == 1) {
distanceMatrix = generateRandomSymmetricDistanceMatrix(numNodes, minDistance, maxDistance);
matrixTypeDescription = "Aleatoria Simétrica";
} else {
distanceMatrix = generateRandomAsymmetricDistanceMatrix(numNodes, minDistance, maxDistance);
matrixTypeDescription = "Aleatoria Asimétrica";
}
System.out.println("Matriz de distancias generada:");
printDistanceMatrix(distanceMatrix, maxDistance);
return new Object[]{distanceMatrix, matrixTypeDescription, minDistance, maxDistance, includeMatrixInReport};
}
private static void configureGAParameters(Scanner scanner, TSPGeneticAlgorithm ga) {
System.out.println("\n--- Configuración del Algoritmo Genético ---");
System.out.print("¿Usar valores por defecto? [S/n]: ");
String useDefaults = scanner.nextLine();
if (useDefaults.equalsIgnoreCase("n")) { // Enter = Sí
ga.populationSize = getIntInputWithDefault(scanner, "Tamaño de la población", ga.populationSize);
ga.numGenerations = getIntInputWithDefault(scanner, "Número de generaciones (límite)", ga.numGenerations);
ga.mutationRate = getDoubleInputWithDefault(scanner, "Tasa de mutación", ga.mutationRate);
} else {
System.out.println("Usando valores por defecto optimizados.");
}
System.out.println("\nParámetros actualizados:");
ga.showCurrentParameters();
}
private static int getIntInputWithDefault(Scanner scanner, String message, int defaultValue) {
while (true) {
System.out.printf("%s [%d]: ", message, defaultValue);
String inputStr = scanner.nextLine();
if (inputStr.isEmpty()) {
return defaultValue;
}
try {
return Integer.parseInt(inputStr);
} catch (NumberFormatException e) {
System.out.println("Entrada inválida. Por favor, ingrese un número entero.");
}
}
}
private static double getDoubleInputWithDefault(Scanner scanner, String message, double defaultValue) {
while (true) {
System.out.printf("%s [%.3f]: ", message, defaultValue);
String inputStr = scanner.nextLine();
if (inputStr.isEmpty()) {
return defaultValue;
}
try {
return Double.parseDouble(inputStr);
} catch (NumberFormatException e) {
System.out.println("Entrada inválida. Por favor, ingrese un número decimal.");
}
}
}
private static String formatElapsedTime(double elapsedTimeMillis) {
double seconds = elapsedTimeMillis / 1000.0;
return String.format("%.3f segundos", seconds);
}
private static void runGeneticAlgorithm(TSPGeneticAlgorithm ga) {
System.out.println("\n--- Ejecución del Algoritmo Genético ---");
System.out.println("Iniciando búsqueda " + (ga.timeLimitMillis > 0
? "con límite de tiempo: " + (ga.timeLimitMillis / 1000) + " segundos"
: "sin límite de tiempo") + "\n");
long startTime = System.currentTimeMillis();
Solution bestSolution = ga.run();
long endTime = System.currentTimeMillis();
double elapsedTimeMillis = endTime - startTime;
System.out.println("\nBúsqueda finalizada.");
System.out.println("Mejor ruta encontrada: " + bestSolution);
System.out.println("Distancia total: " + bestSolution.getDistance());
System.out.println("Tiempo de ejecución: " + formatElapsedTime(elapsedTimeMillis));
System.out.print("¿Guardar informe de ejecución? [S/n]): ");
Scanner scanner = new Scanner(System.in);
String saveOption = scanner.nextLine();
if (!saveOption.equalsIgnoreCase("n")) { // Enter = Sí
String filename = generateReportFilename(ga);
saveExecutionReportToFile(ga, bestSolution, elapsedTimeMillis, filename);
}
}
private static String generateReportFilename(TSPGeneticAlgorithm ga) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
String timestamp = LocalDateTime.now().format(dtf);
String matrixType = ga.matrixTypeDescription.startsWith("Aleatoria Simétrica") ? "S" : "A";
String mutationRateStr = String.format("%04d", (int) (ga.mutationRate * 10000));
String timeLimitStr = (ga.timeLimitMillis > 0) ? String.valueOf(TimeUnit.MILLISECONDS.toSeconds(ga.timeLimitMillis)) : "0";
String randomID = generateRandomString(8);
return String.format("Report_%s_%d%s_P%d_G%d_M%s_T%s_%s.txt",
timestamp,
ga.numCities,
matrixType,
ga.populationSize,
ga.numGenerations,
mutationRateStr,
timeLimitStr,
randomID
);
}
private static String generateRandomString(int length) {
String allowedChars = "abcdefghijklmnopqrstuvwxyz1234567890";
StringBuilder sb = new StringBuilder(length);
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(allowedChars.charAt(random.nextInt(allowedChars.length())));
}
return sb.toString();
}
private static void saveExecutionReportToFile(TSPGeneticAlgorithm ga, Solution solution, double elapsedTimeMillis, String filename) {
try (FileWriter writer = new FileWriter(filename)) {
writer.write("=== INFORME DE EJECUCIÓN TSP ALGORITMO GENÉTICO (AG) ===\n\n");
writer.write("Fecha: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + "\n\n");
writer.write("=== PARÁMETROS AG ===\n\n");
writer.write("Población: " + ga.populationSize + "\n");
writer.write("Generaciones: " + ga.numGenerations + "\n");
writer.write("Mutación: " + ga.mutationRate + "\n");
writer.write("Tiempo límite: " + (ga.timeLimitMillis > 0 ? (ga.timeLimitMillis / 1000) + " s" : "Ilimitado") + "\n\n");
writer.write("=== PARÁMETROS MATRIZ DE DISTANCIAS ===\n\n");
writer.write("Tipo: " + ga.matrixTypeDescription + "\n");
writer.write("Nodos: " + ga.numCities + "\n\n");
if (ga.includeMatrixInReport) {
writer.write("Matriz:\n");
printDistanceMatrix(ga.distanceMatrix, ga.maxDistanceValue, writer);
}
writer.write("\n=== RESULTADOS ===\n\n");
writer.write("Mejor ruta: " + Arrays.toString(solution.getPath()) + "\n");
writer.write("Distancia: " + String.format("%.1f", solution.getDistance()) + "\n");
writer.write("Tiempo ejecución: " + formatElapsedTime(elapsedTimeMillis) + "\n");
System.out.println("Informe guardado como: " + filename);
} catch (IOException e) {
System.out.println("Error al guardar el informe: " + e.getMessage());
}
}
public void showCurrentParameters() {
System.out.println("\n--- PARÁMETROS (AG) ---");
showCurrentParametersAG();
}
public void showCurrentParametersAG() {
System.out.println("Tamaño de población: " + populationSize);
System.out.println("Número de generaciones: " + numGenerations);
System.out.println("Tasa de mutación: " + mutationRate);
}
public Solution run() {
currentMutationRate = mutationRate;
List<Solution> population = initializePopulation();
Solution bestSolution = getFittest(population);
long startTime = System.currentTimeMillis();
generation = 0;
lastImprovement = 0;
while (true) {
boolean timeExpired = (timeLimitMillis > 0) && (System.currentTimeMillis() - startTime >= timeLimitMillis);
if (timeExpired || generation >= numGenerations) {
break;
}
// Elitismo: conservar el 2% de los mejores
int eliteSize = Math.max(1, (int) (populationSize * 0.02));
List<Solution> newPopulation = new ArrayList<>(population.subList(0, eliteSize));
while (newPopulation.size() < populationSize) {
Solution parent1 = tournamentSelection(population);
Solution parent2 = tournamentSelection(population);
Solution offspring = crossoverOperator.crossover(parent1.getPath(), parent2.getPath(), random);
mutate(offspring);
newPopulation.add(offspring);
}
population = new ArrayList<>(newPopulation);
Solution currentBest = getFittest(population);
if (currentBest.getDistance() < bestSolution.getDistance()) {
bestSolution = currentBest;
lastImprovement = generation;
}
// Ajuste adaptativo de mutación
if (generation - lastImprovement > 20) {
mutationRate = Math.min(0.1, mutationRate * 1.2);
}
// Verificación periódica del tiempo
if (generation % 5 == 0 && timeLimitMillis > 0) {
if (System.currentTimeMillis() - startTime >= timeLimitMillis) {
break;
}
}
generation++;
}
return bestSolution;
}
private List<Solution> initializePopulation() {
List<Solution> population = new ArrayList<>();
int[] cities = IntStream.range(0, numCities).toArray();
for (int i = 0; i < populationSize; i++) {
int[] shuffledPath = Arrays.copyOf(cities, cities.length);
shuffleArray(shuffledPath);
population.add(new Solution(shuffledPath, calculateDistance(shuffledPath)));
}
return population;
}
private void shuffleArray(int[] array) {
for (int i = array.length - 1; i > 0; i--) {
int index = random.nextInt(i + 1);
int temp = array[index];
array[index] = array[i];
array[i] = temp;
}
}
private double calculateDistance(int[] path) {
double distance = 0;
for (int i = 0; i < numCities - 1; i++) {
distance += distanceMatrix[path[i]][path[i + 1]];
}
distance += distanceMatrix[path[numCities - 1]][path[0]];
return distance;
}
private Solution tournamentSelection(List<Solution> population) {
int tournamentSize = 3; // Reducido para mayor velocidad
List<Solution> tournament = new ArrayList<>();
for (int i = 0; i < tournamentSize; i++) {
tournament.add(population.get(random.nextInt(population.size())));
}
return getFittest(tournament);
}
private void mutate(Solution solution) {
double dynamicMutationRate = currentMutationRate;
if (generation - lastImprovement > 20) {
currentMutationRate = Math.min(0.1, currentMutationRate * 1.2);
}
if (random.nextDouble() < dynamicMutationRate) {
mutationOperator.mutate(solution.getPath(), random);
solution.setDistance(calculateDistance(solution.getPath()));
}
}
private Solution getFittest(List<Solution> population) {
Solution fittest = population.get(0);
for (Solution solution : population) {
if (solution.getDistance() < fittest.getDistance()) {
fittest = solution;
}
}
return fittest;
}
interface CrossoverOperator {
Solution crossover(int[] parent1, int[] parent2, Random random);
}
interface MutationOperator {
void mutate(int[] path, Random random);
}
static class OrderCrossover implements CrossoverOperator {
private final int[][] distanceMatrix;
public OrderCrossover(int[][] distanceMatrix) {
this.distanceMatrix = distanceMatrix;
}
@Override
public Solution crossover(int[] parent1, int[] parent2, Random random) {
int length = parent1.length;
int[] childPath = new int[length];
Arrays.fill(childPath, -1);
int start = random.nextInt(length);
int end = random.nextInt(length);
if (start > end) {
int temp = start;
start = end;
end = temp;
}
for (int i = start; i <= end; i++) {
childPath[i] = parent1[i];
}
int currentPos = 0;
for (int city : parent2) {
if (!contains(childPath, city)) {
while (currentPos < length && childPath[currentPos] != -1) {
currentPos++;
}
if (currentPos < length) {
childPath[currentPos] = city;
}
}
}
double distance = calculateDistance(childPath);
return new Solution(childPath, distance);
}
private boolean contains(int[] array, int value) {
for (int num : array) {
if (num == value) {
return true;
}
}
return false;
}
private double calculateDistance(int[] path) {
double distance = 0;
for (int i = 0; i < path.length - 1; i++) {
distance += distanceMatrix[path[i]][path[i + 1]];
}
distance += distanceMatrix[path[path.length - 1]][path[0]];
return distance;
}
}
static class SwapMutation implements MutationOperator {
@Override
public void mutate(int[] path, Random random) {
// 70% swap mutation, 30% inversión
if (random.nextDouble() < 0.7) {
// Swap mutation
int index1 = random.nextInt(path.length);
int index2 = random.nextInt(path.length);
int temp = path[index1];
path[index1] = path[index2];
path[index2] = temp;
} else {
// Inversión de segmento
int start = random.nextInt(path.length);
int end = start + random.nextInt(path.length - start);
while (start < end) {
int temp = path[start];
path[start] = path[end];
path[end] = temp;
start++;
end--;
}
}
}
}
static class Solution {
private final int[] path;
private double distance;
public Solution(int[] path, double distance) {
this.path = Arrays.copyOf(path, path.length);
this.distance = distance;
}
public int[] getPath() {
return Arrays.copyOf(path, path.length);
}
public double getDistance() {
return distance;
}
public void setDistance(double distance) {
this.distance = distance;
}
@Override
public String toString() {
return Arrays.toString(path);
}
}
public static int[][] generateRandomSymmetricDistanceMatrix(int numNodes, int minDistance, int maxDistance) {
int[][] matrix = new int[numNodes][numNodes];
Random rand = new Random();
for (int i = 0; i < numNodes; i++) {
for (int j = i + 1; j < numNodes; j++) {
int value = rand.nextInt(maxDistance - minDistance + 1) + minDistance;
matrix[i][j] = value;
matrix[j][i] = value;
}
}
return matrix;
}
public static int[][] generateRandomAsymmetricDistanceMatrix(int numNodes, int minDistance, int maxDistance) {
int[][] matrix = new int[numNodes][numNodes];
Random rand = new Random();
for (int i = 0; i < numNodes; i++) {
for (int j = 0; j < numNodes; j++) {
if (i != j) {
matrix[i][j] = rand.nextInt(maxDistance - minDistance + 1) + minDistance;
}
}
}
return matrix;
}
public static void printDistanceMatrix(int[][] matrix, int maxDistanceValue) {
printDistanceMatrix(matrix, maxDistanceValue, null);
}
private static void printDistanceMatrix(int[][] matrix, int maxDistanceValue, FileWriter writer) {
int numDigits = String.valueOf(maxDistanceValue).length() + 2;
String format = "%" + numDigits + "d";
for (int[] row : matrix) {
StringBuilder sb = new StringBuilder();
for (int val : row) {
sb.append(String.format(format, val));
}
String line = sb.toString();
if (writer == null) {
System.out.println(line);
} else {
try {
writer.write(line + "\n");
} catch (IOException e) {
System.out.println("Error escribiendo en archivo: " + e.getMessage());
}
}
}
}
private void validateMatrix(int[][] matrix) {
if (matrix.length < 4) {
throw new IllegalArgumentException("La matriz debe tener al menos 4 nodos.");
}
for (int i = 0; i < matrix.length; i++) {
if (matrix[i][i] != 0) {
matrix[i][i] = 0;
}
}
}
public void setMatrixGenerationInfo(String type, int min, int max) {
this.matrixTypeDescription = type;
this.minDistanceValue = min;
this.maxDistanceValue = max;
}
public void setIncludeMatrixInReport(boolean include) {
this.includeMatrixInReport = include;
}
}
Resultado:
run:
--- Menú Principal ---
1. Generar matriz de distancias
2. Configurar parámetros del AG
3. Mostrar parámetros actuales
4. Ejecutar algoritmo genético
0. Salir
Seleccione una opción [0-4]: 1
--- Configuración de la Matriz ---
Número de nodos [16]: 8
Tipo de matriz:
1. Aleatoria Simétrica
2. Aleatoria Asimétrica
Seleccione el tipo de matriz [1]: 2
Valor mínimo de la distancia [1]:
Valor máximo de la distancia [1000]:
¿Incluir la matriz en el informe de ejecución? [S/n]: s
Matriz de distancias generada:
0 707 455 183 113 990 675 872
988 0 610 426 600 2 499 696
850 724 0 449 556 847 678 729
820 519 745 0 82 657 755 885
436 665 918 666 0 707 269 794
337 122 607 446 428 0 809 410
501 446 697 542 626 768 0 726
414 711 721 971 832 685 210 0
--- Menú Principal ---
1. Generar matriz de distancias
2. Configurar parámetros del AG
3. Mostrar parámetros actuales
4. Ejecutar algoritmo genético
0. Salir
Seleccione una opción [0-4]: 2
--- Configuración del Algoritmo Genético ---
¿Usar valores por defecto? [S/n]:
Usando valores por defecto optimizados.
Parámetros actualizados:
--- PARÁMETROS (AG) ---
Tamaño de población: 100
Número de generaciones: 1500
Tasa de mutación: 0.04
--- Menú Principal ---
1. Generar matriz de distancias
2. Configurar parámetros del AG
3. Mostrar parámetros actuales
4. Ejecutar algoritmo genético
0. Salir
Seleccione una opción [0-4]: 3
--- MATRIZ DE DISTANCIAS ---
0 707 455 183 113 990 675 872
988 0 610 426 600 2 499 696
850 724 0 449 556 847 678 729
820 519 745 0 82 657 755 885
436 665 918 666 0 707 269 794
337 122 607 446 428 0 809 410
501 446 697 542 626 768 0 726
414 711 721 971 832 685 210 0
Nodos: 8
Rango distancias: [1 - 1000]
Tipo de matriz: Aleatoria Asimétrica
--- PARÁMETROS (AG) ---
Tamaño de población: 100
Número de generaciones: 1500
Tasa de mutación: 0.04
--- Menú Principal ---
1. Generar matriz de distancias
2. Configurar parámetros del AG
3. Mostrar parámetros actuales
4. Ejecutar algoritmo genético
0. Salir
Seleccione una opción [0-4]: 4
Tiempo límite en segundos (0=ilimitado) [10]:
--- Ejecución del Algoritmo Genético ---
Iniciando búsqueda con límite de tiempo: 10 segundos
Búsqueda finalizada.
Mejor ruta encontrada: [2, 3, 4, 6, 1, 5, 7, 0]
Distancia total: 2527.0
Tiempo de ejecución: 0,130 segundos
¿Guardar informe de ejecución? [S/n]):
Informe guardado como: Report_202502012204_8A_P100_G1500_M1000_T10_gpobvzpy.txt
--- Menú Principal ---
1. Generar matriz de distancias
2. Configurar parámetros del AG
3. Mostrar parámetros actuales
4. Ejecutar algoritmo genético
0. Salir
Seleccione una opción [0-4]: 0
Saliendo del programa.
BUILD SUCCESSFUL (total time: 52 seconds)
martes, 27 de agosto de 2024
Juegos VII. La Mansión Misteriosa: Un juego de texto interactivo.
Características:
. El juego utiliza un sistema de escenas conectadas.
. Cada escena tiene una descripción, opciones para el jugador y enlaces a las siguientes escenas.
. El jugador navega por la mansión tomando decisiones en cada escena.
. Las escenas están precargadas en un mapa (java.util.Map).
. El juego comienza con un prólogo y termina cuando el jugador llega a una escena final o de "horror".
Código Java (LaMansionMisteriosa.java):
package lamansionmisteriosa;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class LaMansionMisteriosa {
private static final Scanner scanner = new Scanner(System.in);
private static final Map<String, Escena> escenas = new HashMap<>();
public static void main(String[] args) {
mostrarPrologo();
EscenaLoader.cargarEscenas(escenas);
jugar("start");
}
private static void jugar(String escenaActual) {
while (true) {
Escena escena = escenas.get(escenaActual);
if (escena == null) {
System.out.println("Error: Escena no encontrada. Fin del juego.");
break;
}
System.out.println(escena.getDescripcion());
if (escena.getOpciones().length == 0) {
escenaActual = "horror_final"; // Redirigir a la escena final
escena = escenas.get(escenaActual);
System.out.println(escena.getDescripcion());
System.out.println("FIN DEL JUEGO");
break;
}
for (int i = 0; i < escena.getOpciones().length; i++) {
System.out.println((i + 1) + ". " + escena.getOpciones()[i]);
}
int eleccion;
while (true) {
try {
eleccion = Integer.parseInt(scanner.nextLine()) - 1;
if (eleccion >= 0 && eleccion < escena.getSiguienteEscena().length) {
break;
} else {
System.out.println("Opción no válida. Por favor, elige un número entre 1 y " + escena.getOpciones().length);
}
} catch (NumberFormatException e) {
System.out.println("Por favor, ingresa un número válido.");
}
}
escenaActual = escena.getSiguienteEscena()[eleccion];
}
}
private static void mostrarPrologo() {
System.out.println("La Mansión Misteriosa\n");
System.out.println("Hace muchos años, en una región olvidada por el tiempo, se erige una mansión rodeada de misterio. "
+ "Nadie sabe realmente qué ocurrió allí, pero los rumores hablan de desapariciones, fantasmas y un mal "
+ "inimaginable que acecha en sus pasillos. Ahora, te encuentras frente a esta mansión, dispuesto a descubrir "
+ "la verdad. Pero ten cuidado, ya que esta aventura puede ser tu última...");
System.out.println("\nPresiona Enter para continuar...");
scanner.nextLine(); // Espera a que el jugador presione Enter para continuar
}
}
Código Java (Escena.java):
package lamansionmisteriosa;
class Escena {
private final String descripcion;
private final String[] opciones;
private final String[] siguienteEscena;
public Escena(String descripcion, String[] opciones, String[] siguienteEscena) {
this.descripcion = descripcion;
this.opciones = opciones;
this.siguienteEscena = siguienteEscena;
}
public String getDescripcion() {
return descripcion;
}
public String[] getOpciones() {
return opciones;
}
public String[] getSiguienteEscena() {
return siguienteEscena;
}
}
Código Java (EscenaLoader.java):
package lamansionmisteriosa;
import java.util.Map;
class EscenaLoader {
public static void cargarEscenas(Map<String, Escena> escenas) {
escenas.put("start", new Escena(
"La lluvia golpea con fuerza los ventanales de la vieja mansión. Has oído rumores sobre este lugar, historias de personas que entraron y nunca salieron. Pero tú no crees en fantasmas... ¿verdad?",
new String[]{"Avanzar hacia la puerta principal", "Explorar los alrededores de la mansión"},
new String[]{"puerta_principal", "explorar_alrededores"}
));
escenas.put("puerta_principal", new Escena(
"La puerta de madera cruje mientras la empujas. El vestíbulo está oscuro, con solo la luz de tu linterna iluminando el polvo en el aire. Frente a ti, una escalera se eleva hacia la oscuridad del segundo piso, y un pasillo a la derecha parece perderse en las sombras.",
new String[]{"Subir por las escaleras", "Tomar el pasillo a la derecha"},
new String[]{"escaleras", "pasillo_derecho"}
));
escenas.put("explorar_alrededores", new Escena(
"Decides dar una vuelta alrededor de la mansión antes de entrar. La vegetación está densa y descuidada, y el aire se siente pesado. Llegas a una ventana rota que da a lo que parece ser una cocina.",
new String[]{"Entrar por la ventana rota", "Regresar a la puerta principal"},
new String[]{"cocina", "puerta_principal"}
));
escenas.put("cocina", new Escena(
"La cocina está en ruinas, con platos rotos y utensilios oxidados esparcidos por todos lados. El olor a humedad es casi insoportable. Un ruido sordo proviene de la despensa.",
new String[]{"Abrir la despensa", "Salir de la cocina rápidamente"},
new String[]{"despensa", "volver_vestibulo"}
));
escenas.put("despensa", new Escena(
"Abres la puerta de la despensa, pero está vacía. El ruido cesa de inmediato. Cuando miras hacia atrás, la puerta de la cocina está cerrada y una figura oscura aparece en el umbral...",
new String[]{},
new String[]{"horror_final"}
));
escenas.put("escaleras", new Escena(
"Los escalones crujen bajo tu peso, y cada paso que das resuena como un susurro en la oscuridad. Al llegar al segundo piso, ves tres puertas: una a la izquierda, una a la derecha y una al final del pasillo.",
new String[]{"Abrir la puerta de la izquierda", "Abrir la puerta de la derecha", "Ir hacia la puerta al final del pasillo"},
new String[]{"puerta_izquierda", "puerta_derecha", "puerta_final"}
));
escenas.put("puerta_izquierda", new Escena(
"La puerta se abre con un chirrido. El cuarto está lleno de polvo, con un gran espejo cubierto por una sábana vieja. Mientras te acercas al espejo, algo en él parece moverse...",
new String[]{"Quitar la sábana del espejo", "Salir de la habitación rápidamente"},
new String[]{"espejo", "salir_habitacion"}
));
escenas.put("espejo", new Escena(
"Retiras la sábana, revelando el espejo. Al principio, solo ves tu propio reflejo, pero de repente... una figura oscura aparece detrás de ti en el reflejo. Te das la vuelta, pero no hay nadie ahí. Cuando miras de nuevo al espejo, está roto.",
new String[]{},
new String[]{"horror_final"}
));
escenas.put("puerta_derecha", new Escena(
"Esta puerta conduce a una pequeña biblioteca. Los libros parecen intactos, pero el polvo indica que no han sido tocados en años. De repente, sientes una presencia detrás de ti...",
new String[]{"Ignorar la sensación y examinar los libros", "Voltear rápidamente"},
new String[]{"examinar_libros", "voltear"}
));
escenas.put("examinar_libros", new Escena(
"Pasas tus dedos por los lomos de los libros, pero cuando intentas sacar uno, se atora. Con un poco de esfuerzo, logras sacarlo, y al hacerlo, una sección de la estantería se abre, revelando un pasaje secreto.",
new String[]{"Entrar al pasaje secreto", "No entrar, regresar al vestíbulo"},
new String[]{"pasaje_secreto", "volver_vestibulo"}
));
escenas.put("pasaje_secreto", new Escena(
"Entras en el pasaje secreto, y la puerta de la biblioteca se cierra detrás de ti. El pasaje está oscuro y estrecho, con paredes de piedra fría. Sientes que algo te está observando desde la oscuridad.",
new String[]{"Seguir adelante", "Regresar a la biblioteca"},
new String[]{"seguir_pasaje", "volver_vestibulo"}
));
escenas.put("seguir_pasaje", new Escena(
"El pasaje te lleva a una habitación oculta. En el centro de la habitación hay un libro antiguo sobre un pedestal, cubierto de polvo y telarañas. Cuando te acercas, el libro se abre solo...",
new String[]{},
new String[]{"horror_final"}
));
escenas.put("pasillo_derecho", new Escena(
"El pasillo a la derecha es largo y oscuro, y cada paso que das parece más pesado que el anterior. A medida que avanzas, sientes como si las paredes se cerraran sobre ti. Finalmente, llegas a una puerta pequeña al final del pasillo.",
new String[]{"Abrir la puerta", "Regresar al vestíbulo"},
new String[]{"puerta_final", "volver_vestibulo"}
));
escenas.put("puerta_final", new Escena(
"La puerta al final del pasillo se abre con un rechinido metálico. Dentro, una vieja cuna se mece sola en medio de la habitación.",
new String[]{},
new String[]{"horror_final"}
));
escenas.put("salir_habitacion", new Escena(
"Decides que es mejor no enfrentar lo que sea que esté en esa habitación y te apresuras a salir. El aire en el pasillo es más frío de lo que recuerdas...",
new String[]{},
new String[]{"volver_vestibulo"}
));
escenas.put("volver_vestibulo", new Escena(
"Regresas al vestíbulo, pero algo ha cambiado. La puerta principal, por donde entraste, ahora está cerrada. No hay manera de abrirla...",
new String[]{},
new String[]{"horror_final"}
));
escenas.put("voltear", new Escena(
"Te das la vuelta rápidamente, pero no hay nadie detrás de ti. Sin embargo, sientes como si algo estuviera observándote desde las sombras. Decides que es mejor no arriesgarte y salir de la biblioteca.",
new String[]{},
new String[]{"volver_vestibulo"}
));
escenas.put("horror_final", new Escena(
"La mansión comienza a vibrar suavemente, y los susurros que escuchas se hacen más fuertes. Sabes que debes salir, pero cada salida parece estar bloqueada. Te das cuenta de que la mansión nunca tuvo intención de dejarte ir...\n"
+ "La oscuridad te envuelve por completo y sientes como algo frío y pegajoso te atrapa. Antes de que puedas gritar, todo se vuelve negro...",
new String[]{},
new String[]{}
));
}
}
Resultado:
run:
La Mansión Misteriosa
Hace muchos años, en una región olvidada por el tiempo, se erige una mansión rodeada de misterio. Nadie sabe realmente qué ocurrió allí, pero los rumores hablan de desapariciones, fantasmas y un mal inimaginable que acecha en sus pasillos. Ahora, te encuentras frente a esta mansión, dispuesto a descubrir la verdad. Pero ten cuidado, ya que esta aventura puede ser tu última...
Presiona Enter para continuar...
La lluvia golpea con fuerza los ventanales de la vieja mansión. Has oído rumores sobre este lugar, historias de personas que entraron y nunca salieron. Pero tú no crees en fantasmas... ¿verdad?
1. Avanzar hacia la puerta principal
2. Explorar los alrededores de la mansión
2
Decides dar una vuelta alrededor de la mansión antes de entrar. La vegetación está densa y descuidada, y el aire se siente pesado. Llegas a una ventana rota que da a lo que parece ser una cocina.
1. Entrar por la ventana rota
2. Regresar a la puerta principal
1
La cocina está en ruinas, con platos rotos y utensilios oxidados esparcidos por todos lados. El olor a humedad es casi insoportable. Un ruido sordo proviene de la despensa.
1. Abrir la despensa
2. Salir de la cocina rápidamente
1
Abres la puerta de la despensa, pero está vacía. El ruido cesa de inmediato. Cuando miras hacia atrás, la puerta de la cocina está cerrada y una figura oscura aparece en el umbral...
La mansión comienza a vibrar suavemente, y los susurros que escuchas se hacen más fuertes. Sabes que debes salir, pero cada salida parece estar bloqueada. Te das cuenta de que la mansión nunca tuvo intención de dejarte ir...
La oscuridad te envuelve por completo y sientes como algo frío y pegajoso te atrapa. Antes de que puedas gritar, todo se vuelve negro...
FIN DEL JUEGO
BUILD SUCCESSFUL (total time: 2 minutes 4 seconds)
viernes, 16 de agosto de 2024
Problema del viajante de Comercio TSP (XV). Método Dinámica de Enjambre de Partículas (PSO).
El Algoritmo de Dinámica de Enjambre de Partículas (PSO) aplicado al Problema del Viajante de Comercio (TSP) busca encontrar la ruta más corta posible que visita una serie de ciudades y regresa al punto de partida. Cada partícula en PSO representa una posible ruta a través de todas las ciudades. Estas partículas se mueven dentro del espacio de soluciones ajustando sus rutas basadas en la mejor solución personal que han encontrado y la mejor solución global conocida.
Características:
. Representación de las Soluciones: Cada partícula es una permutación de las ciudades, representando una posible ruta en el TSP.
. Movimiento de las Partículas: Las partículas ajustan su ruta actual usando:
. Inercia (w): Mantienen parte de su ruta anterior.
. Cognitivo (c1): Se acercan a la mejor ruta que han encontrado.
. Social (c2): Se acercan a la mejor ruta conocida por todo el enjambre.
. Exploración y Explotación:
Inercia (w): Un valor moderado, como 0.7, ayuda a balancear entre explorar nuevas rutas y explotar rutas conocidas.
Cognitivo y Social (c1 y c2): Valores iguales, como 1.5, equilibran la influencia de la experiencia personal y la influencia del grupo.
. Actualización de las Rutas: Durante las iteraciones, las partículas intercambian segmentos de rutas para acercarse a las mejores soluciones, evitando quedarse atrapadas en soluciones subóptimas.
. Criterio de Parada: El algoritmo ejecuta un número fijo de iteraciones (por ejemplo, 10000) para asegurar que se explore adecuadamente el espacio de soluciones.
Ventajas:
. Flexibilidad: PSO puede adaptarse fácilmente a problemas con diferentes tamaños de ciudades.
. Capacidad de Escapar de Mínimos Locales: La influencia combinada de la inercia, la mejor solución personal y la global permite que las partículas exploren diferentes áreas del espacio de soluciones.
. Convergencia: PSO tiende a converger hacia una buena solución rápidamente, aunque no garantiza encontrar la solución óptima.
Código Java (TSP_DinamicaEnjambreParticulas.java):
package tsp_dinamicaenjambreparticulas;
import java.util.ArrayList;
public class TSP_DinamicaEnjambreParticulas {
public static void main(String[] args) {
double[][] distanceMatrix = {
{0, 8128, 823, 8767, 1253, 8827, 5106, 5501, 7097, 8026, 7162, 2260, 4441, 510, 7138, 6509},
{8128, 0, 6653, 9241, 4344, 7231, 1503, 7145, 6571, 6217, 9412, 9051, 1931, 5817, 8633, 8513},
{823, 6653, 0, 7900, 1178, 1820, 1262, 678, 9930, 6739, 9965, 908, 9481, 7708, 1874, 9303},
{8767, 9241, 7900, 0, 589, 9827, 520, 6671, 9859, 4489, 1642, 1045, 1760, 5607, 5053, 7776},
{1253, 4344, 1178, 589, 0, 5829, 5823, 3482, 9090, 9537, 8507, 3802, 3302, 2944, 8613, 7257},
{8827, 7231, 1820, 9827, 5829, 0, 9288, 6497, 5442, 4381, 9297, 2086, 1372, 5616, 1981, 3412},
{5106, 1503, 1262, 520, 5823, 9288, 0, 3242, 5625, 4715, 7754, 2366, 8314, 1360, 623, 3915},
{5501, 7145, 678, 6671, 3482, 6497, 3242, 0, 1522, 6648, 324, 8704, 7602, 8091, 1608, 1286},
{7097, 6571, 9930, 9859, 9090, 5442, 5625, 1522, 0, 7740, 7480, 2266, 9346, 967, 9585, 9113},
{8026, 6217, 6739, 4489, 9537, 4381, 4715, 6648, 7740, 0, 9509, 7830, 5276, 7174, 8535, 2604},
{7162, 9412, 9965, 1642, 8507, 9297, 7754, 324, 7480, 9509, 0, 1847, 8948, 9498, 9456, 4716},
{2260, 9051, 908, 1045, 3802, 2086, 2366, 8704, 2266, 7830, 1847, 0, 1620, 6634, 7744, 9744},
{4441, 1931, 9481, 1760, 3302, 1372, 8314, 7602, 9346, 5276, 8948, 1620, 0, 2261, 3892, 7722},
{510, 5817, 7708, 5607, 2944, 5616, 1360, 8091, 967, 7174, 9498, 6634, 2261, 0, 6734, 6447},
{7138, 8633, 1874, 5053, 8613, 1981, 623, 1608, 9585, 8535, 9456, 7744, 3892, 6734, 0, 2382},
{6509, 8513, 9303, 7776, 7257, 3412, 3915, 1286, 9113, 2604, 4716, 9744, 7722, 6447, 2382, 0}
};
int numParticles = 100; // Número partículas
double w = 0.5; // Inercia reducida
double c1 = 2.0; // Coeficiente cognitivo (mayor peso en la mejor solución personal)
double c2 = 2.0; // Coeficiente social (mayor peso en la mejor solución global)
TSPPSO tsp = new TSPPSO(distanceMatrix, numParticles, w, c1, c2);
ArrayList<Integer> bestRoute = tsp.solve(10000); // 10000 iteraciones
double bestDistance = tsp.calculateTotalDistance(bestRoute);
System.out.println("Ruta óptima aproximada encontrada: " + bestRoute);
System.out.println("Distancia mínima aproximada: " + bestDistance);
}
}
Código Java (TSPPSO.java):
package tsp_dinamicaenjambreparticulas;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class TSPPSO {
private final double[][] distanceMatrix;
private final int numCities;
private int numParticles = 50; // Número partículas
private double w = 0.7; // Inercia
private double c1 = 1.5; // Coeficiente cognitivo
private double c2 = 1.5; // Coeficiente social
private final Random random = new Random();
public TSPPSO(double[][] distanceMatrix, int numParticles, double w, double c1, double c2) {
this.distanceMatrix = distanceMatrix;
this.numCities = distanceMatrix.length;
this.numParticles = numParticles;
this.w = w;
this.c1 = c1;
this.c2 = c2;
}
public TSPPSO(double[][] distanceMatrix) {
this.distanceMatrix = distanceMatrix;
this.numCities = distanceMatrix.length;
}
public ArrayList<Integer> solve(int maxIterations) {
ArrayList<ArrayList<Integer>> particles = new ArrayList<>();
ArrayList<ArrayList<Integer>> personalBestPositions = new ArrayList<>();
ArrayList<Double> personalBestDistances = new ArrayList<>();
ArrayList<Integer> globalBestPosition = null;
double globalBestDistance = Double.MAX_VALUE;
// Inicializar las partículas
for (int i = 0; i < numParticles; i++) {
ArrayList<Integer> route = generateRandomRoute();
particles.add(route);
personalBestPositions.add(new ArrayList<>(route));
double routeDistance = calculateTotalDistance(route);
personalBestDistances.add(routeDistance);
// Actualizar mejor global
if (routeDistance < globalBestDistance) {
globalBestPosition = route;
globalBestDistance = routeDistance;
}
}
// Iterar hasta alcanzar el criterio de parada
for (int iter = 0; iter < maxIterations; iter++) {
for (int i = 0; i < numParticles; i++) {
ArrayList<Integer> currentPosition = particles.get(i);
ArrayList<Integer> newPosition = updatePosition(currentPosition, personalBestPositions.get(i), globalBestPosition);
double newDistance = calculateTotalDistance(newPosition);
double personalBestDistance = personalBestDistances.get(i);
// Actualizar la mejor posición personal
if (newDistance < personalBestDistance) {
personalBestPositions.set(i, newPosition);
personalBestDistances.set(i, newDistance);
}
// Actualizar la mejor posición global
if (newDistance < globalBestDistance) {
globalBestPosition = newPosition;
globalBestDistance = newDistance;
}
particles.set(i, newPosition);
}
}
return globalBestPosition;
}
private ArrayList<Integer> generateRandomRoute() {
ArrayList<Integer> route = new ArrayList<>();
for (int i = 0; i < numCities; i++) {
route.add(i);
}
Collections.shuffle(route);
return route;
}
private ArrayList<Integer> updatePosition(ArrayList<Integer> currentPosition, ArrayList<Integer> personalBest, ArrayList<Integer> globalBest) {
ArrayList<Integer> newPosition = new ArrayList<>(currentPosition);
// Aplicar intercambios para simular "velocidades"
for (int i = 0; i < numCities; i++) {
if (random.nextDouble() < w) {
int swapIndex1 = random.nextInt(numCities);
int swapIndex2 = random.nextInt(numCities);
Collections.swap(newPosition, swapIndex1, swapIndex2);
}
if (random.nextDouble() < c1) {
int swapIndex = personalBest.indexOf(newPosition.get(i));
Collections.swap(newPosition, i, swapIndex);
}
if (random.nextDouble() < c2) {
int swapIndex = globalBest.indexOf(newPosition.get(i));
Collections.swap(newPosition, i, swapIndex);
}
}
return newPosition;
}
public double calculateTotalDistance(ArrayList<Integer> route) {
double totalDistance = 0.0;
for (int i = 0; i < route.size() - 1; i++) {
totalDistance += distanceMatrix[route.get(i)][route.get(i + 1)];
}
totalDistance += distanceMatrix[route.get(route.size() - 1)][route.get(0)]; // Retorno al inicio
return totalDistance;
}
}
Resultado:
run:
Ruta óptima aproximada encontrada: [15, 9, 5, 2, 7, 10, 3, 4, 0, 13, 8, 11, 12, 1, 6, 14]
Distancia mínima aproximada: 25093.0
BUILD SUCCESSFUL (total time: 1 second)