Spaces:
Sleeping
Sleeping
import streamlit as st | |
import matplotlib.pyplot as plt | |
import random | |
from mpl_toolkits.mplot3d import Axes3D | |
import plotly.graph_objects as go | |
# Se debe tener instalado plotly, streamlit y matplotlib | |
st.set_page_config(layout="centered", page_title="Secci贸n de pruebas", page_icon="馃К") | |
# Funci贸n para generar una poblaci贸n inicial aleatoria | |
def generar_poblacion(num_individuos, num_ciudades): | |
poblacion = [] | |
for _ in range(num_individuos): | |
individuo = list(range(num_ciudades)) | |
random.shuffle(individuo) | |
poblacion.append(individuo) | |
return poblacion | |
# Funci贸n para evaluar la aptitud de un individuo (distancia total del recorrido) | |
def calcular_aptitud(individuo, distancias): | |
distancia_total = 0 | |
for i in range(len(individuo) - 1): | |
ciudad_actual = individuo[i] | |
siguiente_ciudad = individuo[i + 1] | |
distancia_total += distancias[ciudad_actual][siguiente_ciudad] | |
distancia_total += distancias[individuo[-1]][individuo[0]] | |
return distancia_total | |
# Funci贸n para seleccionar individuos para la reproducci贸n (torneo binario) | |
def seleccion_torneo(poblacion, distancias): | |
seleccionados = [] | |
for _ in range(len(poblacion)): | |
torneo = random.sample(poblacion, 2) | |
aptitud_torneo = [ | |
calcular_aptitud(individuo, distancias) for individuo in torneo | |
] | |
seleccionado = torneo[aptitud_torneo.index(min(aptitud_torneo))] | |
seleccionados.append(seleccionado) | |
return seleccionados | |
checkboxCruce = st.sidebar.checkbox("Sin cruce de caminos") | |
# Funci贸n para realizar el cruce de dos padres para producir un hijo | |
def cruzar(padre1, padre2,distancias): | |
punto_cruce = random.randint(0, len(padre1) - 1) | |
hijo = padre1[:punto_cruce] + [ | |
gen for gen in padre2 if gen not in padre1[:punto_cruce] | |
] | |
# Corregir el camino para evitar cruces | |
if checkboxCruce: | |
hijo = corregir_camino(hijo, distancias) | |
return hijo | |
# Funci贸n para corregir el camino y evitar cruces no deseados | |
def corregir_camino(camino, distancias): | |
n = len(camino) | |
for i in range(n): | |
for j in range(i + 2, n - 1): | |
if distancias[camino[i]][camino[i + 1]] + distancias[camino[j]][camino[j + 1]] > distancias[camino[i]][camino[j]] + distancias[camino[i + 1]][camino[j + 1]]: | |
# Intercambiar las conexiones para corregir el cruce | |
camino[i + 1], camino[j] = camino[j], camino[i + 1] | |
return camino | |
def mostrar_distancias_de_cruce(padre1, padre2, punto_cruce, distancias): | |
genes_cruzados_padre1 = padre1[:punto_cruce] | |
genes_cruzados_padre2 = [gen for gen in padre2 if gen not in genes_cruzados_padre1] | |
distancias_cruce = [ | |
distancias[genes_cruzados_padre1[i]][genes_cruzados_padre2[i]] | |
for i in range(min(len(genes_cruzados_padre1), len(genes_cruzados_padre2))) | |
] | |
print("Distancias entre genes que se cruzan:") | |
for i, distancia in enumerate(distancias_cruce): | |
print(f"Gen {genes_cruzados_padre1[i]} de Padre 1 y Gen {genes_cruzados_padre2[i]} de Padre 2: {distancia:.2f}") | |
# Funci贸n para aplicar mutaciones en la poblaci贸n | |
def mutar(individuo, probabilidad_mutacion): | |
if random.random() < probabilidad_mutacion: | |
indices = random.sample(range(len(individuo)), 2) | |
individuo[indices[0]], individuo[indices[1]] = ( | |
individuo[indices[1]], | |
individuo[indices[0]], | |
) | |
return individuo | |
# Funci贸n para generar distancias aleatorias entre ciudades y sus coordenadas tridimensionales | |
def generar_distancias(num_ciudades): | |
distancias = [[0] * num_ciudades for _ in range(num_ciudades)] | |
coordenadas = [ | |
(random.uniform(0, 100), random.uniform(0, 100), random.uniform(0, 100)) | |
for _ in range(num_ciudades) | |
] | |
for i in range(num_ciudades): | |
for j in range(i + 1, num_ciudades): | |
distancias[i][j] = distancias[j][i] = ( | |
sum((x - y) ** 2 for x, y in zip(coordenadas[i], coordenadas[j])) ** 0.5 | |
) | |
return distancias, coordenadas | |
def visualizar_camino(camino, coordenadas, mejor_distancia): | |
fig = go.Figure() | |
# A帽adir el camino como un trazado 3D interactivo | |
x = [coordenadas[i][0] for i in camino] | |
y = [coordenadas[i][1] for i in camino] | |
z = [coordenadas[i][2] for i in camino] | |
fig.add_trace(go.Scatter3d(x=x, y=y, z=z, mode="lines+markers", name="Camino")) | |
# A帽adir el punto de inicio | |
fig.add_trace( | |
go.Scatter3d( | |
x=[x[0]], | |
y=[y[0]], | |
z=[z[0]], | |
mode="markers", | |
marker=dict(color="green", size=10), | |
name="Inicio", | |
) | |
) | |
# A帽adir el punto de fin | |
fig.add_trace( | |
go.Scatter3d( | |
x=[x[-1]], | |
y=[y[-1]], | |
z=[z[-1]], | |
mode="markers", | |
marker=dict(color="red", size=10), | |
name="Fin", | |
) | |
) | |
# Configuraciones adicionales | |
fig.update_layout( | |
scene=dict(aspectmode="cube"), | |
title=f"Mejor Camino Encontrado\nDistancia: {mejor_distancia:.2f}", | |
) | |
# Mostrar el gr谩fico interactivo en Streamlit | |
st.plotly_chart(fig) | |
def visualizar_camino_streamlit(camino, coordenadas, mejor_distancia): | |
fig_camino = go.Figure() | |
# A帽adir el camino como un trazado 3D interactivo | |
x = [coordenadas[i][0] for i in camino] | |
y = [coordenadas[i][1] for i in camino] | |
z = [coordenadas[i][2] for i in camino] | |
# A帽adir el camino como un trazado 3D interactivo con identificadores | |
fig_camino.add_trace( | |
go.Scatter3d( | |
x=x, y=y, z=z, mode="lines+markers", marker=dict(size=5), name="Camino" | |
) | |
) | |
# A帽adir los puntos de inicio y fin con etiquetas | |
fig_camino.add_trace( | |
go.Scatter3d( | |
x=[x[0]], | |
y=[y[0]], | |
z=[z[0]], | |
mode="markers+text", | |
marker=dict(color="green", size=10), | |
name="Inicio", | |
text=[str(camino[0])], | |
textposition="top center", | |
) | |
) | |
fig_camino.add_trace( | |
go.Scatter3d( | |
x=[x[-1]], | |
y=[y[-1]], | |
z=[z[-1]], | |
mode="markers+text", | |
marker=dict(color="red", size=10), | |
name="Fin", | |
text=[str(camino[-1])], | |
textposition="top center", | |
) | |
) | |
# A帽adir etiquetas a los puntos intermedios | |
for i, (xi, yi, zi) in enumerate(zip(x[1:-1], y[1:-1], z[1:-1])): | |
fig_camino.add_trace( | |
go.Scatter3d( | |
x=[xi], | |
y=[yi], | |
z=[zi], | |
mode="markers+text", | |
marker=dict(size=5), | |
text=[str(camino[i + 1])], | |
textposition="top center", | |
) | |
) | |
# Configuraciones adicionales | |
fig_camino.update_layout( | |
scene=dict(aspectmode="cube"), | |
title=f"Mejor Camino Encontrado\nDistancia: {mejor_distancia:.2f}", | |
) | |
# Mostrar el gr谩fico interactivo en Streamlit | |
st.plotly_chart(fig_camino) | |
def fitness(distancia, maxima_distancia, tamCromosoma): | |
return 1 - ((distancia) / (maxima_distancia * tamCromosoma)) | |
def algoritmo_genetico( | |
num_generaciones, | |
num_ciudades, | |
num_individuos, | |
probabilidad_mutacion, | |
distancias, | |
probabilidad_cruce, | |
): | |
poblacion = generar_poblacion(num_individuos, num_ciudades) | |
mejor_solucion_historial = [] | |
mejor_distancia_historial = [] | |
peor = 0 | |
fitness_historial = [] | |
for _ in range(num_generaciones): | |
poblacion = sorted(poblacion, key=lambda x: calcular_aptitud(x, distancias)) | |
mejor_individuo = poblacion[0] | |
mejor_distancia = calcular_aptitud(mejor_individuo, distancias) | |
# Almacenar el mejor individuo y su distancia en cada generaci贸n | |
mejor_solucion_historial.append(mejor_individuo) | |
mejor_distancia_historial.append(mejor_distancia) | |
seleccionados = seleccion_torneo(poblacion, distancias) | |
nueva_poblacion = [] | |
for i in range(0, len(seleccionados), 2): | |
padre1, padre2 = seleccionados[i], seleccionados[i + 1] | |
aleatorio_local = random.uniform(0, 1) | |
if aleatorio_local <= probabilidad_cruce: | |
hijo1 = cruzar(padre1, padre2, distancias) | |
hijo2 = cruzar(padre2, padre1, distancias) | |
else: | |
hijo1, hijo2 = padre1, padre2 | |
hijo1 = mutar(hijo1, probabilidad_mutacion) | |
hijo2 = mutar(hijo2, probabilidad_mutacion) | |
nueva_poblacion.extend([hijo1, hijo2]) | |
poblacion = nueva_poblacion | |
if peor < mejor_distancia: | |
peor = mejor_distancia | |
# print(peor,fitness(mejor_distancia,peor,len(padre1))) | |
fitness_historial.append(fitness(mejor_distancia, peor, len(padre1))) | |
mejor_solucion = poblacion[0] | |
mejor_distancia = calcular_aptitud(mejor_solucion, distancias) | |
# Visualizar el proceso del algoritmo | |
visualizar_proceso_streamlit( | |
mejor_distancia_historial, mejor_solucion, coordenadas, mejor_distancia | |
) | |
# visualizar el fitness | |
visualizar_proceso_fitness_streamlit(fitness_historial) | |
# Visualizar el mejor camino encontrado | |
visualizar_camino_streamlit(mejor_solucion, coordenadas, mejor_distancia) | |
return mejor_solucion, mejor_distancia | |
def visualizar_proceso_fitness_streamlit(fitness_arreglo): | |
generaciones = list(range(len(fitness_arreglo))) | |
fig_fitness = go.Figure() | |
fig_fitness.add_trace( | |
go.Scatter(x=generaciones, y=fitness_arreglo, mode="lines+markers") | |
) | |
fig_fitness.update_layout( | |
title="Evoluci贸n del fitnesss en Cada Generaci贸n", | |
xaxis_title="Generaci贸n", | |
yaxis_title="fitness", | |
) | |
st.plotly_chart(fig_fitness) | |
def visualizar_proceso_streamlit( | |
mejor_distancia_historial, mejor_solucion, coordenadas, mejor_distancia | |
): | |
generaciones = list(range(len(mejor_distancia_historial))) | |
# Crear gr谩fico interactivo de evoluci贸n de la distancia | |
fig_distancia = go.Figure() | |
fig_distancia.add_trace( | |
go.Scatter(x=generaciones, y=mejor_distancia_historial, mode="lines+markers") | |
) | |
fig_distancia.update_layout( | |
title="Evoluci贸n de la Distancia en Cada Generaci贸n", | |
xaxis_title="Generaci贸n", | |
yaxis_title="Distancia", | |
) | |
st.plotly_chart(fig_distancia) | |
def algoritmo_genetico_early_stopping( | |
num_generaciones, | |
num_ciudades, | |
num_individuos, | |
probabilidad_mutacion, | |
distancias, | |
probabilidad_cruce, | |
max_generaciones_sin_mejora, | |
): | |
poblacion = generar_poblacion(num_individuos, num_ciudades) | |
mejor_solucion_historial = [] | |
mejor_distancia_historial = [] | |
peor = 0 | |
fitness_historial = [] | |
generaciones_sin_mejora = 0 # Inicializar contador de generaciones sin mejora | |
for _ in range(num_generaciones): | |
poblacion = sorted(poblacion, key=lambda x: calcular_aptitud(x, distancias)) | |
mejor_individuo = poblacion[0] | |
mejor_distancia = calcular_aptitud(mejor_individuo, distancias) | |
# Almacenar el mejor individuo y su distancia en cada generaci贸n | |
mejor_solucion_historial.append(mejor_individuo) | |
mejor_distancia_historial.append(mejor_distancia) | |
seleccionados = seleccion_torneo(poblacion, distancias) | |
nueva_poblacion = [] | |
for i in range(0, len(seleccionados), 2): | |
padre1, padre2 = seleccionados[i], seleccionados[i + 1] | |
aleatorio_local = random.uniform(0, 1) | |
if aleatorio_local <= probabilidad_cruce: | |
hijo1 = cruzar(padre1, padre2, distancias) | |
hijo2 = cruzar(padre2, padre1, distancias) | |
else: | |
hijo1, hijo2 = padre1, padre2 | |
hijo1 = mutar(hijo1, probabilidad_mutacion) | |
hijo2 = mutar(hijo2, probabilidad_mutacion) | |
nueva_poblacion.extend([hijo1, hijo2]) | |
poblacion = nueva_poblacion | |
if peor < mejor_distancia: | |
peor = mejor_distancia | |
generaciones_sin_mejora = 0 # Reiniciar contador al mejorar | |
else: | |
generaciones_sin_mejora += 1 | |
# Verificar Early Stopping | |
if generaciones_sin_mejora >= max_generaciones_sin_mejora: | |
st.warning( | |
"Early Stopping: Se detuvo el algoritmo debido a falta de mejora." | |
) | |
break | |
fitness_historial.append(fitness(mejor_distancia, peor, len(padre1))) | |
mejor_solucion = poblacion[0] | |
mejor_distancia = calcular_aptitud(mejor_solucion, distancias) | |
# Visualizar el proceso del algoritmo | |
visualizar_proceso_streamlit( | |
mejor_distancia_historial, mejor_solucion, coordenadas, mejor_distancia | |
) | |
# visualizar el fitness | |
visualizar_proceso_fitness_streamlit(fitness_historial) | |
# Visualizar el mejor camino encontrado | |
visualizar_camino_streamlit(mejor_solucion, coordenadas, mejor_distancia) | |
return mejor_solucion, mejor_distancia | |
# Ejemplo de uso en Streamlit | |
if __name__ == "__main__": | |
st.title("Algoritmo Gen茅tico para el Problema del Viajante") | |
st.sidebar.header("Configuraci贸n") | |
num_ciudades = st.sidebar.number_input( | |
"N煤mero de Ciudades", min_value=5, max_value=100, value=10, step=5 | |
) | |
num_generaciones = st.sidebar.number_input( | |
"N煤mero de Generaciones", min_value=10, max_value=1000, value=50, step=10 | |
) | |
num_individuos = st.sidebar.slider( | |
"Tama帽o de la Poblaci贸n", min_value=10, max_value=100, value=50, step=2 | |
) | |
probabilidad_mutacion = st.sidebar.slider( | |
"Probabilidad de Mutaci贸n", min_value=0.01, max_value=0.1, value=0.01 | |
) | |
probabilidad_cruce = st.sidebar.slider( | |
"Probabilidad de Cruce", min_value=0.9, max_value=1.0, value=0.95, step=0.01 | |
) | |
# Generar distancias aleatorias entre las ciudades y sus coordenadas tridimensionales | |
distancias, coordenadas = generar_distancias(num_ciudades) | |
checkbox = st.sidebar.checkbox("Early Stopping") | |
if checkbox: | |
max_generaciones_sin_mejora = st.sidebar.number_input( | |
"Limite de generaciones sin mejora", | |
min_value=5, | |
max_value=1000, | |
value=20, | |
step=5, | |
) | |
mejor_solucion, mejor_distancia = algoritmo_genetico_early_stopping( | |
num_generaciones, | |
num_ciudades, | |
num_individuos, | |
probabilidad_mutacion, | |
distancias, | |
probabilidad_cruce, | |
max_generaciones_sin_mejora, | |
) | |
else: | |
mejor_solucion, mejor_distancia = algoritmo_genetico( | |
num_generaciones, | |
num_ciudades, | |
num_individuos, | |
probabilidad_mutacion, | |
distancias, | |
probabilidad_cruce, | |
) | |
st.success(f"Mejor soluci贸n encontrada: {mejor_solucion}") | |
st.success(f"Mejor distancia encontrada: {mejor_distancia:.2f}") | |