JairoDanielMT commited on
Commit
6469a13
1 Parent(s): 050142e

2D y 3D más una descripción

Browse files
Files changed (3) hide show
  1. app.py +30 -299
  2. pages/AG_2D.py +306 -0
  3. 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
- # 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):
23
- distancia_total = 0
24
- for i in range(len(individuo) - 1):
25
- ciudad_actual = individuo[i]
26
- siguiente_ciudad = individuo[i + 1]
27
- distancia_total += distancias[ciudad_actual][siguiente_ciudad]
28
- distancia_total += distancias[individuo[-1]][individuo[0]]
29
- return distancia_total
30
-
31
-
32
- # Función para seleccionar individuos para la reproducción (torneo binario)
33
- def seleccion_torneo(poblacion, distancias):
34
- seleccionados = []
35
- for _ in range(len(poblacion)):
36
- torneo = random.sample(poblacion, 2)
37
- aptitud_torneo = [
38
- calcular_aptitud(individuo, distancias) for individuo in torneo
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}")