Spaces:
Sleeping
Sleeping
JairoDanielMT
commited on
Commit
•
6469a13
1
Parent(s):
050142e
2D y 3D más una descripción
Browse files- app.py +30 -299
- pages/AG_2D.py +306 -0
- pages/AG_3D.py +348 -0
app.py
CHANGED
@@ -6,302 +6,33 @@ import plotly.graph_objects as go
|
|
6 |
import pandas as pd
|
7 |
|
8 |
# Se debe tener instalado plotly, streamlit y matplotlib
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
]
|
40 |
-
seleccionado = torneo[aptitud_torneo.index(min(aptitud_torneo))]
|
41 |
-
seleccionados.append(seleccionado)
|
42 |
-
return seleccionados
|
43 |
-
|
44 |
-
|
45 |
-
# Función para realizar el cruce de dos padres para producir un hijo
|
46 |
-
def cruzar(padre1, padre2):
|
47 |
-
punto_cruce = random.randint(0, len(padre1) - 1)
|
48 |
-
hijo = padre1[:punto_cruce] + [
|
49 |
-
gen for gen in padre2 if gen not in padre1[:punto_cruce]
|
50 |
-
]
|
51 |
-
return hijo
|
52 |
-
|
53 |
-
|
54 |
-
# Función para aplicar mutaciones en la población
|
55 |
-
def mutar(individuo, probabilidad_mutacion):
|
56 |
-
if random.random() < probabilidad_mutacion:
|
57 |
-
indices = random.sample(range(len(individuo)), 2)
|
58 |
-
individuo[indices[0]], individuo[indices[1]] = (
|
59 |
-
individuo[indices[1]],
|
60 |
-
individuo[indices[0]],
|
61 |
-
)
|
62 |
-
return individuo
|
63 |
-
|
64 |
-
|
65 |
-
# Función para generar distancias aleatorias entre ciudades y sus coordenadas tridimensionales
|
66 |
-
def generar_distancias(num_ciudades):
|
67 |
-
distancias = [[0] * num_ciudades for _ in range(num_ciudades)]
|
68 |
-
coordenadas = [
|
69 |
-
(random.uniform(0, 100), random.uniform(0, 100), random.uniform(0, 100))
|
70 |
-
for _ in range(num_ciudades)
|
71 |
-
]
|
72 |
-
|
73 |
-
for i in range(num_ciudades):
|
74 |
-
for j in range(i + 1, num_ciudades):
|
75 |
-
distancias[i][j] = distancias[j][i] = (
|
76 |
-
sum((x - y) ** 2 for x, y in zip(coordenadas[i], coordenadas[j])) ** 0.5
|
77 |
-
)
|
78 |
-
|
79 |
-
return distancias, coordenadas
|
80 |
-
|
81 |
-
|
82 |
-
def visualizar_camino(camino, coordenadas, mejor_distancia):
|
83 |
-
fig = go.Figure()
|
84 |
-
|
85 |
-
# Añadir el camino como un trazado 3D interactivo
|
86 |
-
x = [coordenadas[i][0] for i in camino]
|
87 |
-
y = [coordenadas[i][1] for i in camino]
|
88 |
-
z = [coordenadas[i][2] for i in camino]
|
89 |
-
|
90 |
-
fig.add_trace(go.Scatter3d(x=x, y=y, z=z, mode="lines+markers", name="Camino"))
|
91 |
-
|
92 |
-
# Añadir el punto de inicio
|
93 |
-
fig.add_trace(
|
94 |
-
go.Scatter3d(
|
95 |
-
x=[x[0]],
|
96 |
-
y=[y[0]],
|
97 |
-
z=[z[0]],
|
98 |
-
mode="markers",
|
99 |
-
marker=dict(color="green", size=10),
|
100 |
-
name="Inicio",
|
101 |
-
)
|
102 |
-
)
|
103 |
-
|
104 |
-
# Añadir el punto de fin
|
105 |
-
fig.add_trace(
|
106 |
-
go.Scatter3d(
|
107 |
-
x=[x[-1]],
|
108 |
-
y=[y[-1]],
|
109 |
-
z=[z[-1]],
|
110 |
-
mode="markers",
|
111 |
-
marker=dict(color="red", size=10),
|
112 |
-
name="Fin",
|
113 |
-
)
|
114 |
-
)
|
115 |
-
|
116 |
-
# Configuraciones adicionales
|
117 |
-
fig.update_layout(
|
118 |
-
scene=dict(aspectmode="cube"),
|
119 |
-
title=f"Mejor Camino Encontrado\nDistancia: {mejor_distancia:.2f}",
|
120 |
-
)
|
121 |
-
|
122 |
-
# Mostrar el gráfico interactivo en Streamlit
|
123 |
-
st.plotly_chart(fig)
|
124 |
-
|
125 |
-
|
126 |
-
def visualizar_camino_streamlit(camino, coordenadas, mejor_distancia):
|
127 |
-
fig_camino = go.Figure()
|
128 |
-
|
129 |
-
# Añadir el camino como un trazado 3D interactivo
|
130 |
-
x = [coordenadas[i][0] for i in camino]
|
131 |
-
y = [coordenadas[i][1] for i in camino]
|
132 |
-
z = [coordenadas[i][2] for i in camino]
|
133 |
-
|
134 |
-
# Añadir el camino como un trazado 3D interactivo con identificadores
|
135 |
-
fig_camino.add_trace(
|
136 |
-
go.Scatter3d(
|
137 |
-
x=x, y=y, z=z, mode="lines+markers", marker=dict(size=5), name="Camino"
|
138 |
-
)
|
139 |
-
)
|
140 |
-
|
141 |
-
# Añadir los puntos de inicio y fin con etiquetas
|
142 |
-
fig_camino.add_trace(
|
143 |
-
go.Scatter3d(
|
144 |
-
x=[x[0]],
|
145 |
-
y=[y[0]],
|
146 |
-
z=[z[0]],
|
147 |
-
mode="markers+text",
|
148 |
-
marker=dict(color="green", size=10),
|
149 |
-
name="Inicio",
|
150 |
-
text=[str(camino[0])],
|
151 |
-
textposition="top center",
|
152 |
-
)
|
153 |
-
)
|
154 |
-
|
155 |
-
fig_camino.add_trace(
|
156 |
-
go.Scatter3d(
|
157 |
-
x=[x[-1]],
|
158 |
-
y=[y[-1]],
|
159 |
-
z=[z[-1]],
|
160 |
-
mode="markers+text",
|
161 |
-
marker=dict(color="red", size=10),
|
162 |
-
name="Fin",
|
163 |
-
text=[str(camino[-1])],
|
164 |
-
textposition="top center",
|
165 |
-
)
|
166 |
-
)
|
167 |
-
|
168 |
-
# Añadir etiquetas a los puntos intermedios
|
169 |
-
for i, (xi, yi, zi) in enumerate(zip(x[1:-1], y[1:-1], z[1:-1])):
|
170 |
-
fig_camino.add_trace(
|
171 |
-
go.Scatter3d(
|
172 |
-
x=[xi],
|
173 |
-
y=[yi],
|
174 |
-
z=[zi],
|
175 |
-
mode="markers+text",
|
176 |
-
marker=dict(size=5),
|
177 |
-
text=[str(camino[i + 1])],
|
178 |
-
textposition="top center",
|
179 |
-
)
|
180 |
-
)
|
181 |
-
|
182 |
-
# Configuraciones adicionales
|
183 |
-
fig_camino.update_layout(
|
184 |
-
scene=dict(aspectmode="cube"),
|
185 |
-
title=f"Mejor Camino Encontrado\nDistancia: {mejor_distancia:.2f}",
|
186 |
-
)
|
187 |
-
|
188 |
-
# Mostrar el gráfico interactivo en Streamlit
|
189 |
-
st.plotly_chart(fig_camino)
|
190 |
-
|
191 |
-
|
192 |
-
def algoritmo_genetico(
|
193 |
-
num_generaciones, num_ciudades, num_individuos, probabilidad_mutacion, distancias
|
194 |
-
):
|
195 |
-
poblacion = generar_poblacion(num_individuos, num_ciudades)
|
196 |
-
mejor_solucion_historial = []
|
197 |
-
mejor_distancia_historial = []
|
198 |
-
for generacion in range(num_generaciones):
|
199 |
-
poblacion = sorted(poblacion, key=lambda x: calcular_aptitud(x, distancias))
|
200 |
-
mejor_individuo = poblacion[0]
|
201 |
-
mejor_distancia = calcular_aptitud(mejor_individuo, distancias)
|
202 |
-
mejor_solucion_historial.append(mejor_individuo)
|
203 |
-
mejor_distancia_historial.append(mejor_distancia)
|
204 |
-
seleccionados = seleccion_torneo(poblacion, distancias)
|
205 |
-
nueva_poblacion = []
|
206 |
-
for i in range(0, len(seleccionados), 2):
|
207 |
-
padre1, padre2 = seleccionados[i], seleccionados[i + 1]
|
208 |
-
hijo1 = cruzar(padre1, padre2)
|
209 |
-
hijo2 = cruzar(padre2, padre1)
|
210 |
-
hijo1 = mutar(hijo1, probabilidad_mutacion)
|
211 |
-
hijo2 = mutar(hijo2, probabilidad_mutacion)
|
212 |
-
nueva_poblacion.extend([hijo1, hijo2])
|
213 |
-
poblacion = nueva_poblacion
|
214 |
-
mejor_solucion = poblacion[0]
|
215 |
-
mejor_distancia = calcular_aptitud(mejor_solucion, distancias)
|
216 |
-
# Visualizar el proceso del algoritmo
|
217 |
-
visualizar_proceso_streamlit(
|
218 |
-
mejor_distancia_historial, mejor_solucion, coordenadas, mejor_distancia
|
219 |
-
)
|
220 |
-
# Visualizar el mejor camino encontrado
|
221 |
-
visualizar_camino_streamlit(mejor_solucion, coordenadas, mejor_distancia)
|
222 |
-
|
223 |
-
return mejor_solucion, mejor_distancia
|
224 |
-
|
225 |
-
|
226 |
-
def visualizar_proceso_streamlit(
|
227 |
-
mejor_distancia_historial, mejor_solucion, coordenadas, mejor_distancia
|
228 |
-
):
|
229 |
-
generaciones = list(range(len(mejor_distancia_historial)))
|
230 |
-
|
231 |
-
# Crear gráfico interactivo de evolución de la distancia
|
232 |
-
fig_distancia = go.Figure()
|
233 |
-
fig_distancia.add_trace(
|
234 |
-
go.Scatter(x=generaciones, y=mejor_distancia_historial, mode="lines+markers")
|
235 |
-
)
|
236 |
-
fig_distancia.update_layout(
|
237 |
-
title="Evolución de la Distancia en Cada Generación",
|
238 |
-
xaxis_title="Generación",
|
239 |
-
yaxis_title="Distancia",
|
240 |
-
)
|
241 |
-
st.plotly_chart(fig_distancia)
|
242 |
-
|
243 |
-
|
244 |
-
if __name__ == "__main__":
|
245 |
-
st.title("Algoritmo Genético para el Problema del Viajante")
|
246 |
-
st.sidebar.header("Configuración")
|
247 |
-
manual_input = st.sidebar.checkbox("Ingresar datos manualmente")
|
248 |
-
# Ingresar datos manualmente o generarlos aleatoriamente (por defecto), además de generar las distancias
|
249 |
-
if manual_input:
|
250 |
-
st.sidebar.subheader("Ingresar coordenadas manualmente:")
|
251 |
-
num_ciudades = st.sidebar.number_input(
|
252 |
-
"Número de Ciudades", min_value=5, max_value=100, value=10, step=5
|
253 |
-
)
|
254 |
-
|
255 |
-
coordenadas = []
|
256 |
-
for i in range(num_ciudades):
|
257 |
-
st.sidebar.subheader(f"Coordenadas Ciudad {i + 1}")
|
258 |
-
x = st.sidebar.number_input(
|
259 |
-
f"X Ciudad {i + 1}:", value=0.0, key=f"x_{i}", step=1.0
|
260 |
-
)
|
261 |
-
y = st.sidebar.number_input(
|
262 |
-
f"Y Ciudad {i + 1}:", value=0.0, key=f"y_{i}", step=1.0
|
263 |
-
)
|
264 |
-
z = st.sidebar.number_input(
|
265 |
-
f"Z Ciudad {i + 1}:", value=0.0, key=f"z_{i}", step=1.0
|
266 |
-
)
|
267 |
-
coordenadas.append((x, y, z))
|
268 |
-
|
269 |
-
data = pd.DataFrame(coordenadas, columns=["X", "Y", "Z"])
|
270 |
-
distancias, _ = generar_distancias(num_ciudades)
|
271 |
-
else:
|
272 |
-
num_ciudades = st.sidebar.number_input(
|
273 |
-
"Número de Ciudades", min_value=5, max_value=100, value=10, step=5
|
274 |
-
)
|
275 |
-
|
276 |
-
distancias, coordenadas = generar_distancias(num_ciudades)
|
277 |
-
data = pd.DataFrame(coordenadas, columns=["X", "Y", "Z"])
|
278 |
-
|
279 |
-
# Mostrar datos en la interfaz
|
280 |
-
st.write(
|
281 |
-
"Puntos en el espacio tridimensional (X, Y, Z), donde cada punto es una ciudad"
|
282 |
-
)
|
283 |
-
st.dataframe(data=data, use_container_width=True)
|
284 |
-
|
285 |
-
# Configuraciones adicionales
|
286 |
-
num_generaciones = st.sidebar.slider(
|
287 |
-
"Número de Generaciones", min_value=10, max_value=100, value=50
|
288 |
-
)
|
289 |
-
num_individuos = st.sidebar.slider(
|
290 |
-
"Tamaño de la Población ($par$)", min_value=10, max_value=100, value=50, step=2
|
291 |
-
)
|
292 |
-
probabilidad_mutacion = st.sidebar.slider(
|
293 |
-
"Probabilidad de Mutación", min_value=0.01, max_value=0.5, value=0.1
|
294 |
-
)
|
295 |
-
|
296 |
-
# Ejecutar el algoritmo genético
|
297 |
-
mejor_solucion, mejor_distancia = algoritmo_genetico(
|
298 |
-
num_generaciones,
|
299 |
-
num_ciudades,
|
300 |
-
num_individuos,
|
301 |
-
probabilidad_mutacion,
|
302 |
-
distancias,
|
303 |
-
)
|
304 |
-
|
305 |
-
# Mostrar resultados
|
306 |
-
st.success(f"Mejor solución encontrada: {mejor_solucion}")
|
307 |
-
st.success(f"Mejor distancia encontrada: {mejor_distancia:.2f}")
|
|
|
6 |
import pandas as pd
|
7 |
|
8 |
# Se debe tener instalado plotly, streamlit y matplotlib
|
9 |
+
# configurar con wide mode
|
10 |
+
st.set_page_config(layout="wide", page_title="AG for TSP", page_icon="🧬")
|
11 |
+
|
12 |
+
st.sidebar.title("Algoritmo Genético para el Problema del Viajante")
|
13 |
+
st.sidebar.write("Integrantes: ")
|
14 |
+
st.sidebar.write("🦁 Edson Emanuel Alvarado Prieto ")
|
15 |
+
st.sidebar.write("🐺 Jairo Daniel Mendoza Torres ")
|
16 |
+
st.write(
|
17 |
+
"""
|
18 |
+
## **Descripción:**
|
19 |
+
Este fascinante código despliega un Algoritmo Genético (AG) diseñado para abordar el intrigante Problema del Viajante, también conocido como el Problema del Viajante de Comercio (TSP en inglés), llevando la experiencia a un entorno bidimensional y tridimensional. El TSP desafía a encontrar el camino más corto que visite cada ciudad exactamente una vez y regrese al punto de origen, persiguiendo la meta de minimizar la distancia total recorrida.
|
20 |
+
|
21 |
+
## **Uso:**
|
22 |
+
1. Ofrece la opción de ingresar manualmente las coordenadas de las ciudades o generarlas de manera aleatoria.
|
23 |
+
2. La interfaz de Streamlit permite ajustar parámetros como el número de ciudades, el tamaño de la población, el número de generaciones y la probabilidad de mutación.
|
24 |
+
3. Al ejecutar el código, se despliega una visualización de la evolución del mejor camino encontrado a lo largo de las generaciones, así como la representación gráfica del mejor camino y su distancia final.
|
25 |
+
|
26 |
+
## **Aplicaciones:**
|
27 |
+
- 🚚 **Logística y Distribución:** Optimización de rutas para vehículos de entrega, agilizando la logística empresarial.
|
28 |
+
- 🗺️ **Planificación de Circuitos Electrónicos:** Conexión eficiente de componentes en placas de circuito, impulsando la ingeniería electrónica.
|
29 |
+
- 🧬 **Biología Molecular:** Determinación de la secuencia genética más eficiente en el mapeo genético, contribuyendo a avances en biotecnología.
|
30 |
+
- 🛠️ **Planificación de Proyectos:** Optimización de la secuencia de tareas para minimizar el tiempo total del proyecto, mejorando la gestión de proyectos.
|
31 |
+
|
32 |
+
## 🌐 **Puntos a tocar:**
|
33 |
+
- 🧬 Explora el Problema del Viajante.
|
34 |
+
- 🚚 Optimiza Rutas en 2D y 3D.
|
35 |
+
- 🗺️ Considerar ingreso de coordenadas manual o aleatorio
|
36 |
+
- 🧠 Utiliza un Algoritmo Genético para soluciones inteligentes y eficaces.
|
37 |
+
"""
|
38 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pages/AG_2D.py
ADDED
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import random
|
3 |
+
import plotly.graph_objects as go
|
4 |
+
import pandas as pd
|
5 |
+
|
6 |
+
# Se debe tener instalado plotly, streamlit y matplotlib
|
7 |
+
|
8 |
+
st.set_page_config(layout="centered", page_title="AG for TSP 2D", page_icon="🧬"
|
9 |
+
)
|
10 |
+
|
11 |
+
# Función para generar una población inicial aleatoria
|
12 |
+
def generar_poblacion(num_individuos, num_ciudades):
|
13 |
+
poblacion = []
|
14 |
+
for _ in range(num_individuos):
|
15 |
+
individuo = list(range(num_ciudades))
|
16 |
+
random.shuffle(individuo)
|
17 |
+
poblacion.append(individuo)
|
18 |
+
return poblacion
|
19 |
+
|
20 |
+
|
21 |
+
# Función para evaluar la aptitud de un individuo (distancia total del recorrido)
|
22 |
+
def calcular_aptitud(individuo, distancias, coordenadas):
|
23 |
+
distancia_total = 0
|
24 |
+
coordenadas_iguales = all(coord == coordenadas[0] for coord in coordenadas)
|
25 |
+
|
26 |
+
if not coordenadas_iguales:
|
27 |
+
for i in range(len(individuo) - 1):
|
28 |
+
ciudad_actual = individuo[i]
|
29 |
+
siguiente_ciudad = individuo[i + 1]
|
30 |
+
distancia_total += distancias[ciudad_actual][siguiente_ciudad]
|
31 |
+
|
32 |
+
distancia_total += distancias[individuo[-1]][individuo[0]]
|
33 |
+
|
34 |
+
return distancia_total
|
35 |
+
|
36 |
+
|
37 |
+
# Función para seleccionar individuos para la reproducción (torneo binario)
|
38 |
+
def seleccion_torneo(poblacion, distancias, coordenadas):
|
39 |
+
seleccionados = []
|
40 |
+
for _ in range(len(poblacion)):
|
41 |
+
torneo = random.sample(poblacion, 2)
|
42 |
+
aptitud_torneo = [
|
43 |
+
calcular_aptitud(individuo, distancias, coordenadas) for individuo in torneo
|
44 |
+
]
|
45 |
+
seleccionado = torneo[aptitud_torneo.index(min(aptitud_torneo))]
|
46 |
+
seleccionados.append(seleccionado)
|
47 |
+
return seleccionados
|
48 |
+
|
49 |
+
|
50 |
+
# Función para realizar el cruce de dos padres para producir un hijo
|
51 |
+
def cruzar(padre1, padre2):
|
52 |
+
punto_cruce = random.randint(0, len(padre1) - 1)
|
53 |
+
hijo = padre1[:punto_cruce] + [
|
54 |
+
gen for gen in padre2 if gen not in padre1[:punto_cruce]
|
55 |
+
]
|
56 |
+
return hijo
|
57 |
+
|
58 |
+
|
59 |
+
# Función para aplicar mutaciones en la población
|
60 |
+
def mutar(individuo, probabilidad_mutacion):
|
61 |
+
if random.random() < probabilidad_mutacion:
|
62 |
+
indices = random.sample(range(len(individuo)), 2)
|
63 |
+
individuo[indices[0]], individuo[indices[1]] = (
|
64 |
+
individuo[indices[1]],
|
65 |
+
individuo[indices[0]],
|
66 |
+
)
|
67 |
+
return individuo
|
68 |
+
|
69 |
+
|
70 |
+
# Función para generar distancias aleatorias entre ciudades y sus coordenadas bidimensionales
|
71 |
+
def generar_distancias(num_ciudades):
|
72 |
+
distancias = [[0] * num_ciudades for _ in range(num_ciudades)]
|
73 |
+
coordenadas = [
|
74 |
+
(random.uniform(0, 100), random.uniform(0, 100)) for _ in range(num_ciudades)
|
75 |
+
]
|
76 |
+
|
77 |
+
for i in range(num_ciudades):
|
78 |
+
for j in range(i + 1, num_ciudades):
|
79 |
+
distancias[i][j] = distancias[j][i] = (
|
80 |
+
sum((x - y) ** 2 for x, y in zip(coordenadas[i], coordenadas[j])) ** 0.5
|
81 |
+
)
|
82 |
+
|
83 |
+
return distancias, coordenadas
|
84 |
+
|
85 |
+
|
86 |
+
# Función para visualizar el camino en 2D
|
87 |
+
def visualizar_camino(camino, coordenadas, mejor_distancia):
|
88 |
+
fig = go.Figure()
|
89 |
+
|
90 |
+
# Añadir el camino como un trazado 2D interactivo
|
91 |
+
x = [coordenadas[i][0] for i in camino]
|
92 |
+
y = [coordenadas[i][1] for i in camino]
|
93 |
+
|
94 |
+
fig.add_trace(go.Scatter(x=x, y=y, mode="lines+markers", name="Camino"))
|
95 |
+
|
96 |
+
# Añadir el punto de inicio
|
97 |
+
fig.add_trace(
|
98 |
+
go.Scatter(
|
99 |
+
x=[x[0]],
|
100 |
+
y=[y[0]],
|
101 |
+
mode="markers",
|
102 |
+
marker=dict(color="green", size=10),
|
103 |
+
name="Inicio",
|
104 |
+
)
|
105 |
+
)
|
106 |
+
|
107 |
+
# Añadir el punto de fin
|
108 |
+
fig.add_trace(
|
109 |
+
go.Scatter(
|
110 |
+
x=[x[-1]],
|
111 |
+
y=[y[-1]],
|
112 |
+
mode="markers",
|
113 |
+
marker=dict(color="red", size=10),
|
114 |
+
name="Fin",
|
115 |
+
)
|
116 |
+
)
|
117 |
+
|
118 |
+
# Configuraciones adicionales
|
119 |
+
fig.update_layout(
|
120 |
+
title=f"Mejor Camino Encontrado\nDistancia: {mejor_distancia:.2f}"
|
121 |
+
)
|
122 |
+
|
123 |
+
# Mostrar el gráfico interactivo en Streamlit
|
124 |
+
st.plotly_chart(fig)
|
125 |
+
|
126 |
+
|
127 |
+
def visualizar_camino_streamlit(camino, coordenadas, mejor_distancia):
|
128 |
+
fig_camino = go.Figure()
|
129 |
+
|
130 |
+
# Añadir el camino como un trazado 2D interactivo
|
131 |
+
x = [coordenadas[i][0] for i in camino]
|
132 |
+
y = [coordenadas[i][1] for i in camino]
|
133 |
+
|
134 |
+
# Añadir el camino como un trazado 2D interactivo con identificadores
|
135 |
+
fig_camino.add_trace(
|
136 |
+
go.Scatter(x=x, y=y, mode="lines+markers", marker=dict(size=5), name="Camino")
|
137 |
+
)
|
138 |
+
|
139 |
+
# Añadir los puntos de inicio y fin con etiquetas
|
140 |
+
fig_camino.add_trace(
|
141 |
+
go.Scatter(
|
142 |
+
x=[x[0]],
|
143 |
+
y=[y[0]],
|
144 |
+
mode="markers+text",
|
145 |
+
marker=dict(color="green", size=10),
|
146 |
+
name="Inicio",
|
147 |
+
text=[str(camino[0])],
|
148 |
+
textposition="top center",
|
149 |
+
)
|
150 |
+
)
|
151 |
+
|
152 |
+
fig_camino.add_trace(
|
153 |
+
go.Scatter(
|
154 |
+
x=[x[-1]],
|
155 |
+
y=[y[-1]],
|
156 |
+
mode="markers+text",
|
157 |
+
marker=dict(color="red", size=10),
|
158 |
+
name="Fin",
|
159 |
+
text=[str(camino[-1])],
|
160 |
+
textposition="top center",
|
161 |
+
)
|
162 |
+
)
|
163 |
+
|
164 |
+
# Añadir etiquetas a los puntos intermedios
|
165 |
+
for i, (xi, yi) in enumerate(zip(x[1:-1], y[1:-1])):
|
166 |
+
fig_camino.add_trace(
|
167 |
+
go.Scatter(
|
168 |
+
x=[xi],
|
169 |
+
y=[yi],
|
170 |
+
mode="markers+text",
|
171 |
+
marker=dict(size=5),
|
172 |
+
text=[str(camino[i + 1])],
|
173 |
+
textposition="top center",
|
174 |
+
)
|
175 |
+
)
|
176 |
+
|
177 |
+
# Configuraciones adicionales
|
178 |
+
fig_camino.update_layout(
|
179 |
+
title=f"Mejor Camino Encontrado\nDistancia: {mejor_distancia:.2f}"
|
180 |
+
)
|
181 |
+
|
182 |
+
# Mostrar el gráfico interactivo en Streamlit
|
183 |
+
st.plotly_chart(fig_camino)
|
184 |
+
|
185 |
+
|
186 |
+
def algoritmo_genetico(
|
187 |
+
num_generaciones,
|
188 |
+
num_ciudades,
|
189 |
+
num_individuos,
|
190 |
+
probabilidad_mutacion,
|
191 |
+
distancias,
|
192 |
+
coordenadas,
|
193 |
+
):
|
194 |
+
poblacion = generar_poblacion(num_individuos, num_ciudades)
|
195 |
+
mejor_solucion_historial = []
|
196 |
+
mejor_distancia_historial = []
|
197 |
+
for generacion in range(num_generaciones):
|
198 |
+
poblacion = sorted(
|
199 |
+
poblacion, key=lambda x: calcular_aptitud(x, distancias, coordenadas)
|
200 |
+
)
|
201 |
+
mejor_individuo = poblacion[0]
|
202 |
+
mejor_distancia = calcular_aptitud(mejor_individuo, distancias, coordenadas)
|
203 |
+
mejor_solucion_historial.append(mejor_individuo)
|
204 |
+
mejor_distancia_historial.append(mejor_distancia)
|
205 |
+
seleccionados = seleccion_torneo(poblacion, distancias, coordenadas)
|
206 |
+
nueva_poblacion = []
|
207 |
+
for i in range(0, len(seleccionados), 2):
|
208 |
+
padre1, padre2 = seleccionados[i], seleccionados[i + 1]
|
209 |
+
hijo1 = cruzar(padre1, padre2)
|
210 |
+
hijo2 = cruzar(padre2, padre1)
|
211 |
+
hijo1 = mutar(hijo1, probabilidad_mutacion)
|
212 |
+
hijo2 = mutar(hijo2, probabilidad_mutacion)
|
213 |
+
nueva_poblacion.extend([hijo1, hijo2])
|
214 |
+
poblacion = nueva_poblacion
|
215 |
+
mejor_solucion = poblacion[0]
|
216 |
+
mejor_distancia = calcular_aptitud(mejor_solucion, distancias, coordenadas)
|
217 |
+
# Visualizar el proceso del algoritmo
|
218 |
+
visualizar_proceso_streamlit(
|
219 |
+
mejor_distancia_historial, mejor_solucion, coordenadas, mejor_distancia
|
220 |
+
)
|
221 |
+
# Visualizar el mejor camino encontrado
|
222 |
+
visualizar_camino_streamlit(mejor_solucion, coordenadas, mejor_distancia)
|
223 |
+
|
224 |
+
return mejor_solucion, mejor_distancia
|
225 |
+
|
226 |
+
|
227 |
+
def visualizar_proceso_streamlit(
|
228 |
+
mejor_distancia_historial, mejor_solucion, coordenadas, mejor_distancia
|
229 |
+
):
|
230 |
+
generaciones = list(range(len(mejor_distancia_historial)))
|
231 |
+
|
232 |
+
# Crear gráfico interactivo de evolución de la distancia
|
233 |
+
fig_distancia = go.Figure()
|
234 |
+
fig_distancia.add_trace(
|
235 |
+
go.Scatter(x=generaciones, y=mejor_distancia_historial, mode="lines+markers")
|
236 |
+
)
|
237 |
+
fig_distancia.update_layout(
|
238 |
+
title="Evolución de la Distancia en Cada Generación",
|
239 |
+
xaxis_title="Generación",
|
240 |
+
yaxis_title="Distancia",
|
241 |
+
)
|
242 |
+
st.plotly_chart(fig_distancia)
|
243 |
+
|
244 |
+
|
245 |
+
if __name__ == "__main__":
|
246 |
+
st.title("Algoritmo Genético para el Problema del Viajante")
|
247 |
+
st.sidebar.header("Configuración")
|
248 |
+
manual_input = st.sidebar.checkbox("Ingresar datos manualmente")
|
249 |
+
# Ingresar datos manualmente o generarlos aleatoriamente (por defecto), además de generar las distancias
|
250 |
+
if manual_input:
|
251 |
+
st.sidebar.subheader("Ingresar coordenadas manualmente:")
|
252 |
+
num_ciudades = st.sidebar.number_input(
|
253 |
+
"Número de Ciudades", min_value=5, max_value=100, value=10, step=5
|
254 |
+
)
|
255 |
+
|
256 |
+
coordenadas = []
|
257 |
+
for i in range(num_ciudades):
|
258 |
+
st.sidebar.subheader(f"Coordenadas Ciudad {i + 1}")
|
259 |
+
x = st.sidebar.number_input(
|
260 |
+
f"X Ciudad {i + 1}:", value=0.0, key=f"x_{i}", step=1.0
|
261 |
+
)
|
262 |
+
y = st.sidebar.number_input(
|
263 |
+
f"Y Ciudad {i + 1}:", value=0.0, key=f"y_{i}", step=1.0
|
264 |
+
)
|
265 |
+
coordenadas.append((x, y))
|
266 |
+
|
267 |
+
data = pd.DataFrame(coordenadas, columns=["X", "Y"])
|
268 |
+
distancias, _ = generar_distancias(num_ciudades)
|
269 |
+
else:
|
270 |
+
num_ciudades = st.sidebar.number_input(
|
271 |
+
"Número de Ciudades", min_value=5, max_value=100, value=10, step=5
|
272 |
+
)
|
273 |
+
|
274 |
+
distancias, coordenadas = generar_distancias(num_ciudades)
|
275 |
+
data = pd.DataFrame(coordenadas, columns=["X", "Y"])
|
276 |
+
|
277 |
+
# Mostrar datos en la interfaz
|
278 |
+
st.write(
|
279 |
+
"Puntos en el espacio bidimensional (X, Y), donde cada punto es una ciudad"
|
280 |
+
)
|
281 |
+
st.dataframe(data=data, use_container_width=True)
|
282 |
+
|
283 |
+
# Configuraciones adicionales
|
284 |
+
num_generaciones = st.sidebar.slider(
|
285 |
+
"Número de Generaciones", min_value=10, max_value=100, value=50
|
286 |
+
)
|
287 |
+
num_individuos = st.sidebar.slider(
|
288 |
+
"Tamaño de la Población ($par$)", min_value=10, max_value=100, value=50, step=2
|
289 |
+
)
|
290 |
+
probabilidad_mutacion = st.sidebar.slider(
|
291 |
+
"Probabilidad de Mutación", min_value=0.01, max_value=0.5, value=0.1
|
292 |
+
)
|
293 |
+
|
294 |
+
# Ejecutar el algoritmo genético
|
295 |
+
mejor_solucion, mejor_distancia = algoritmo_genetico(
|
296 |
+
num_generaciones,
|
297 |
+
num_ciudades,
|
298 |
+
num_individuos,
|
299 |
+
probabilidad_mutacion,
|
300 |
+
distancias,
|
301 |
+
coordenadas,
|
302 |
+
)
|
303 |
+
|
304 |
+
# Mostrar resultados
|
305 |
+
st.success(f"Mejor solución encontrada: {mejor_solucion}")
|
306 |
+
st.success(f"Mejor distancia encontrada: {mejor_distancia:.2f}")
|
pages/AG_3D.py
ADDED
@@ -0,0 +1,348 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import matplotlib.pyplot as plt
|
3 |
+
import random
|
4 |
+
from mpl_toolkits.mplot3d import Axes3D
|
5 |
+
import plotly.graph_objects as go
|
6 |
+
import pandas as pd
|
7 |
+
|
8 |
+
# Se debe tener instalado plotly, streamlit y matplotlib
|
9 |
+
st.set_page_config(layout="centered", page_title="AG for TSP 3D", page_icon="🧬")
|
10 |
+
|
11 |
+
|
12 |
+
# Función para generar una población inicial aleatoria
|
13 |
+
def generar_poblacion(num_individuos, num_ciudades):
|
14 |
+
poblacion = []
|
15 |
+
for _ in range(num_individuos):
|
16 |
+
individuo = list(range(num_ciudades))
|
17 |
+
random.shuffle(individuo)
|
18 |
+
poblacion.append(individuo)
|
19 |
+
return poblacion
|
20 |
+
|
21 |
+
|
22 |
+
# Función para evaluar la aptitud de un individuo (distancia total del recorrido)
|
23 |
+
# Función para evaluar la aptitud de un individuo (distancia total del recorrido)
|
24 |
+
def calcular_aptitud(individuo, distancias, coordenadas):
|
25 |
+
distancia_total = 0
|
26 |
+
|
27 |
+
# Verificar si todas las coordenadas de inicio son cero
|
28 |
+
coordenadas_inicio_cero = all(coord == 0 for coord in coordenadas[0])
|
29 |
+
|
30 |
+
if not coordenadas_inicio_cero:
|
31 |
+
for i in range(len(individuo) - 1):
|
32 |
+
ciudad_actual = individuo[i]
|
33 |
+
siguiente_ciudad = individuo[i + 1]
|
34 |
+
distancia_total += distancias[ciudad_actual][siguiente_ciudad]
|
35 |
+
distancia_total += distancias[individuo[-1]][individuo[0]]
|
36 |
+
|
37 |
+
return distancia_total
|
38 |
+
|
39 |
+
|
40 |
+
# Función para seleccionar individuos para la reproducción (torneo binario)
|
41 |
+
def seleccion_torneo(poblacion, distancias, coordenadas):
|
42 |
+
seleccionados = []
|
43 |
+
for _ in range(len(poblacion)):
|
44 |
+
torneo = random.sample(poblacion, 2)
|
45 |
+
aptitud_torneo = [
|
46 |
+
calcular_aptitud(individuo, distancias, coordenadas) for individuo in torneo
|
47 |
+
]
|
48 |
+
seleccionado = torneo[aptitud_torneo.index(min(aptitud_torneo))]
|
49 |
+
seleccionados.append(seleccionado)
|
50 |
+
return seleccionados
|
51 |
+
|
52 |
+
|
53 |
+
# Función para realizar el cruce de dos padres para producir un hijo
|
54 |
+
def cruzar(padre1, padre2):
|
55 |
+
punto_cruce = random.randint(0, len(padre1) - 1)
|
56 |
+
hijo = padre1[:punto_cruce] + [
|
57 |
+
gen for gen in padre2 if gen not in padre1[:punto_cruce]
|
58 |
+
]
|
59 |
+
return hijo
|
60 |
+
|
61 |
+
|
62 |
+
# Función para aplicar mutaciones en la población
|
63 |
+
def mutar(individuo, probabilidad_mutacion):
|
64 |
+
if random.random() < probabilidad_mutacion:
|
65 |
+
indices = random.sample(range(len(individuo)), 2)
|
66 |
+
individuo[indices[0]], individuo[indices[1]] = (
|
67 |
+
individuo[indices[1]],
|
68 |
+
individuo[indices[0]],
|
69 |
+
)
|
70 |
+
return individuo
|
71 |
+
|
72 |
+
|
73 |
+
# Función para generar distancias aleatorias entre ciudades y sus coordenadas tridimensionales
|
74 |
+
def generar_distancias(num_ciudades):
|
75 |
+
distancias = [[0] * num_ciudades for _ in range(num_ciudades)]
|
76 |
+
|
77 |
+
# Verificar si todas las coordenadas de inicio son cero
|
78 |
+
coordenadas_inicio_cero = all(coord == 0 for coord in coordenadas[0])
|
79 |
+
|
80 |
+
if not coordenadas_inicio_cero:
|
81 |
+
for i in range(num_ciudades):
|
82 |
+
for j in range(i + 1, num_ciudades):
|
83 |
+
distancias[i][j] = distancias[j][i] = (
|
84 |
+
sum((x - y) ** 2 for x, y in zip(coordenadas[i], coordenadas[j]))
|
85 |
+
** 0.5
|
86 |
+
)
|
87 |
+
|
88 |
+
return distancias
|
89 |
+
|
90 |
+
|
91 |
+
def visualizar_camino(camino, coordenadas, mejor_distancia):
|
92 |
+
fig = go.Figure()
|
93 |
+
|
94 |
+
# Añadir el camino como un trazado 3D interactivo
|
95 |
+
x = [coordenadas[i][0] for i in camino]
|
96 |
+
y = [coordenadas[i][1] for i in camino]
|
97 |
+
z = [coordenadas[i][2] for i in camino]
|
98 |
+
|
99 |
+
fig.add_trace(go.Scatter3d(x=x, y=y, z=z, mode="lines+markers", name="Camino"))
|
100 |
+
|
101 |
+
# Añadir el punto de inicio
|
102 |
+
fig.add_trace(
|
103 |
+
go.Scatter3d(
|
104 |
+
x=[x[0]],
|
105 |
+
y=[y[0]],
|
106 |
+
z=[z[0]],
|
107 |
+
mode="markers",
|
108 |
+
marker=dict(color="green", size=10),
|
109 |
+
name="Inicio",
|
110 |
+
)
|
111 |
+
)
|
112 |
+
|
113 |
+
# Añadir el punto de fin
|
114 |
+
fig.add_trace(
|
115 |
+
go.Scatter3d(
|
116 |
+
x=[x[-1]],
|
117 |
+
y=[y[-1]],
|
118 |
+
z=[z[-1]],
|
119 |
+
mode="markers",
|
120 |
+
marker=dict(color="red", size=10),
|
121 |
+
name="Fin",
|
122 |
+
)
|
123 |
+
)
|
124 |
+
|
125 |
+
# Configuraciones adicionales
|
126 |
+
fig.update_layout(
|
127 |
+
scene=dict(aspectmode="cube"),
|
128 |
+
title=f"Mejor Camino Encontrado\nDistancia: {mejor_distancia:.2f}",
|
129 |
+
)
|
130 |
+
|
131 |
+
# Mostrar el gráfico interactivo en Streamlit
|
132 |
+
st.plotly_chart(fig)
|
133 |
+
|
134 |
+
|
135 |
+
def visualizar_camino_streamlit(camino, coordenadas, mejor_distancia):
|
136 |
+
fig_camino = go.Figure()
|
137 |
+
|
138 |
+
# Añadir el camino como un trazado 3D interactivo
|
139 |
+
x = [coordenadas[i][0] for i in camino]
|
140 |
+
y = [coordenadas[i][1] for i in camino]
|
141 |
+
z = [coordenadas[i][2] for i in camino]
|
142 |
+
|
143 |
+
# Añadir el camino como un trazado 3D interactivo con identificadores
|
144 |
+
fig_camino.add_trace(
|
145 |
+
go.Scatter3d(
|
146 |
+
x=x, y=y, z=z, mode="lines+markers", marker=dict(size=5), name="Camino"
|
147 |
+
)
|
148 |
+
)
|
149 |
+
|
150 |
+
# Añadir los puntos de inicio y fin con etiquetas
|
151 |
+
fig_camino.add_trace(
|
152 |
+
go.Scatter3d(
|
153 |
+
x=[x[0]],
|
154 |
+
y=[y[0]],
|
155 |
+
z=[z[0]],
|
156 |
+
mode="markers+text",
|
157 |
+
marker=dict(color="green", size=10),
|
158 |
+
name="Inicio",
|
159 |
+
text=[str(camino[0])],
|
160 |
+
textposition="top center",
|
161 |
+
)
|
162 |
+
)
|
163 |
+
|
164 |
+
fig_camino.add_trace(
|
165 |
+
go.Scatter3d(
|
166 |
+
x=[x[-1]],
|
167 |
+
y=[y[-1]],
|
168 |
+
z=[z[-1]],
|
169 |
+
mode="markers+text",
|
170 |
+
marker=dict(color="red", size=10),
|
171 |
+
name="Fin",
|
172 |
+
text=[str(camino[-1])],
|
173 |
+
textposition="top center",
|
174 |
+
)
|
175 |
+
)
|
176 |
+
|
177 |
+
# Añadir etiquetas a los puntos intermedios
|
178 |
+
for i, (xi, yi, zi) in enumerate(zip(x[1:-1], y[1:-1], z[1:-1])):
|
179 |
+
fig_camino.add_trace(
|
180 |
+
go.Scatter3d(
|
181 |
+
x=[xi],
|
182 |
+
y=[yi],
|
183 |
+
z=[zi],
|
184 |
+
mode="markers+text",
|
185 |
+
marker=dict(size=5),
|
186 |
+
text=[str(camino[i + 1])],
|
187 |
+
textposition="top center",
|
188 |
+
)
|
189 |
+
)
|
190 |
+
|
191 |
+
# Configuraciones adicionales
|
192 |
+
fig_camino.update_layout(
|
193 |
+
scene=dict(aspectmode="cube"),
|
194 |
+
title=f"Mejor Camino Encontrado\nDistancia: {mejor_distancia:.2f}",
|
195 |
+
)
|
196 |
+
|
197 |
+
# Mostrar el gráfico interactivo en Streamlit
|
198 |
+
st.plotly_chart(fig_camino)
|
199 |
+
|
200 |
+
|
201 |
+
# ...
|
202 |
+
|
203 |
+
|
204 |
+
def algoritmo_genetico(
|
205 |
+
num_generaciones,
|
206 |
+
num_ciudades,
|
207 |
+
num_individuos,
|
208 |
+
probabilidad_mutacion,
|
209 |
+
distancias,
|
210 |
+
coordenadas,
|
211 |
+
):
|
212 |
+
poblacion = generar_poblacion(num_individuos, num_ciudades)
|
213 |
+
mejor_solucion_historial = []
|
214 |
+
mejor_distancia_historial = []
|
215 |
+
for generacion in range(num_generaciones):
|
216 |
+
poblacion = sorted(
|
217 |
+
poblacion, key=lambda x: calcular_aptitud(x, distancias, coordenadas)
|
218 |
+
)
|
219 |
+
mejor_individuo = poblacion[0]
|
220 |
+
mejor_distancia = calcular_aptitud(mejor_individuo, distancias, coordenadas)
|
221 |
+
mejor_solucion_historial.append(mejor_individuo)
|
222 |
+
mejor_distancia_historial.append(mejor_distancia)
|
223 |
+
seleccionados = seleccion_torneo(poblacion, distancias, coordenadas)
|
224 |
+
nueva_poblacion = []
|
225 |
+
for i in range(0, len(seleccionados), 2):
|
226 |
+
padre1, padre2 = seleccionados[i], seleccionados[i + 1]
|
227 |
+
hijo1 = cruzar(padre1, padre2)
|
228 |
+
hijo2 = cruzar(padre2, padre1)
|
229 |
+
hijo1 = mutar(hijo1, probabilidad_mutacion)
|
230 |
+
hijo2 = mutar(hijo2, probabilidad_mutacion)
|
231 |
+
nueva_poblacion.extend([hijo1, hijo2])
|
232 |
+
poblacion = nueva_poblacion
|
233 |
+
mejor_solucion = poblacion[0]
|
234 |
+
mejor_distancia = calcular_aptitud(mejor_solucion, distancias, coordenadas)
|
235 |
+
# Visualizar el proceso del algoritmo
|
236 |
+
visualizar_proceso_streamlit(
|
237 |
+
mejor_distancia_historial, mejor_solucion, coordenadas, mejor_distancia
|
238 |
+
)
|
239 |
+
# Visualizar el mejor camino encontrado
|
240 |
+
visualizar_camino_streamlit(mejor_solucion, coordenadas, mejor_distancia)
|
241 |
+
|
242 |
+
return mejor_solucion, mejor_distancia
|
243 |
+
|
244 |
+
|
245 |
+
def visualizar_proceso_streamlit(
|
246 |
+
mejor_distancia_historial, mejor_solucion, coordenadas, mejor_distancia
|
247 |
+
):
|
248 |
+
generaciones = list(range(len(mejor_distancia_historial)))
|
249 |
+
|
250 |
+
# Crear gráfico interactivo de evolución de la distancia
|
251 |
+
fig_distancia = go.Figure()
|
252 |
+
fig_distancia.add_trace(
|
253 |
+
go.Scatter(x=generaciones, y=mejor_distancia_historial, mode="lines+markers")
|
254 |
+
)
|
255 |
+
fig_distancia.update_layout(
|
256 |
+
title="Evolución de la Distancia en Cada Generación",
|
257 |
+
xaxis_title="Generación",
|
258 |
+
yaxis_title="Distancia",
|
259 |
+
)
|
260 |
+
st.plotly_chart(fig_distancia)
|
261 |
+
|
262 |
+
|
263 |
+
# Función para generar distancias aleatorias entre ciudades y sus coordenadas tridimensionales
|
264 |
+
def generar_distancias_coordenadas(num_ciudades):
|
265 |
+
distancias = [[0] * num_ciudades for _ in range(num_ciudades)]
|
266 |
+
coordenadas = [
|
267 |
+
(random.uniform(0, 100), random.uniform(0, 100), random.uniform(0, 100))
|
268 |
+
for _ in range(num_ciudades)
|
269 |
+
]
|
270 |
+
|
271 |
+
for i in range(num_ciudades):
|
272 |
+
for j in range(i + 1, num_ciudades):
|
273 |
+
distancias[i][j] = distancias[j][i] = (
|
274 |
+
sum((x - y) ** 2 for x, y in zip(coordenadas[i], coordenadas[j])) ** 0.5
|
275 |
+
)
|
276 |
+
|
277 |
+
return distancias, coordenadas
|
278 |
+
|
279 |
+
|
280 |
+
if __name__ == "__main__":
|
281 |
+
st.title("Algoritmo Genético para el Problema del Viajante")
|
282 |
+
st.sidebar.header("Configuración")
|
283 |
+
manual_input = st.sidebar.checkbox("Ingresar datos manualmente")
|
284 |
+
|
285 |
+
# Ingresar datos manualmente o generarlos aleatoriamente (por defecto), además de generar las distancias
|
286 |
+
if manual_input:
|
287 |
+
st.sidebar.subheader("Ingresar coordenadas manualmente:")
|
288 |
+
num_ciudades = st.sidebar.number_input(
|
289 |
+
"Número de Ciudades", min_value=5, max_value=100, value=10, step=5
|
290 |
+
)
|
291 |
+
|
292 |
+
coordenadas = []
|
293 |
+
for i in range(num_ciudades):
|
294 |
+
st.sidebar.subheader(f"Coordenadas Ciudad {i + 1}")
|
295 |
+
x = st.sidebar.number_input(
|
296 |
+
f"X Ciudad {i + 1}:", value=0.0, key=f"x_{i}", step=1.0
|
297 |
+
)
|
298 |
+
y = st.sidebar.number_input(
|
299 |
+
f"Y Ciudad {i + 1}:", value=0.0, key=f"y_{i}", step=1.0
|
300 |
+
)
|
301 |
+
z = st.sidebar.number_input(
|
302 |
+
f"Z Ciudad {i + 1}:", value=0.0, key=f"z_{i}", step=1.0
|
303 |
+
)
|
304 |
+
coordenadas.append((x, y, z))
|
305 |
+
|
306 |
+
data = pd.DataFrame(coordenadas, columns=["X", "Y", "Z"])
|
307 |
+
distancias = generar_distancias(num_ciudades)
|
308 |
+
else:
|
309 |
+
num_ciudades = st.sidebar.number_input(
|
310 |
+
"Número de Ciudades", min_value=5, max_value=100, value=10, step=5
|
311 |
+
)
|
312 |
+
|
313 |
+
coordenadas = generar_distancias_coordenadas(num_ciudades)[1]
|
314 |
+
distancias = generar_distancias(num_ciudades)
|
315 |
+
|
316 |
+
# Mostrar datos en la interfaz
|
317 |
+
st.write(
|
318 |
+
"Puntos en el espacio tridimensional (X, Y, Z), donde cada punto es una ciudad"
|
319 |
+
)
|
320 |
+
st.dataframe(
|
321 |
+
data=pd.DataFrame(coordenadas, columns=["X", "Y", "Z"]),
|
322 |
+
use_container_width=True,
|
323 |
+
)
|
324 |
+
|
325 |
+
# Configuraciones adicionales
|
326 |
+
num_generaciones = st.sidebar.slider(
|
327 |
+
"Número de Generaciones", min_value=10, max_value=100, value=50
|
328 |
+
)
|
329 |
+
num_individuos = st.sidebar.slider(
|
330 |
+
"Tamaño de la Población ($par$)", min_value=10, max_value=100, value=50, step=2
|
331 |
+
)
|
332 |
+
probabilidad_mutacion = st.sidebar.slider(
|
333 |
+
"Probabilidad de Mutación", min_value=0.01, max_value=0.5, value=0.1
|
334 |
+
)
|
335 |
+
|
336 |
+
# Ejecutar el algoritmo genético
|
337 |
+
mejor_solucion, mejor_distancia = algoritmo_genetico(
|
338 |
+
num_generaciones,
|
339 |
+
num_ciudades,
|
340 |
+
num_individuos,
|
341 |
+
probabilidad_mutacion,
|
342 |
+
distancias,
|
343 |
+
coordenadas, # Se añade este argumento
|
344 |
+
)
|
345 |
+
|
346 |
+
# Mostrar resultados
|
347 |
+
st.success(f"Mejor solución encontrada: {mejor_solucion}")
|
348 |
+
st.success(f"Mejor distancia encontrada: {mejor_distancia:.2f}")
|