Aprendizaje Interactivo de Teoría de Grafos
Aprendizaje Interactivo de Teoría de Grafos
Guest User
Using app without sign in
Visualizador interactivo de pathfinding A*
Encuentra el camino más corto más rápido que Dijkstra guiando la búsqueda con una heurística
Selecciona un algoritmo y genera pasos para comenzar la visualización
A* encuentra el camino más barato entre dos puntos de un grafo ponderado y es el algoritmo detrás del pathfinding en la mayoría de videojuegos y robots. Es Dijkstra con un añadido: una estimación de cuánto falta desde cada nodo hasta la meta, que empuja la búsqueda hacia el destino en lugar de extenderse por igual en todas direcciones. Hart, Nilsson y Raphael lo publicaron en 1968.
Cada nodo lleva tres números: g, el coste confirmado desde el origen; h, el coste estimado que queda; y f = g + h, el total estimado. A* mantiene un conjunto abierto de nodos descubiertos y siempre expande el de menor f. Expandir significa moverlo al conjunto cerrado y relajar sus aristas exactamente como haría Dijkstra. La búsqueda termina en cuanto se expande la meta. Con un montículo binario esto corre en O((V + E) log V), la misma cota que Dijkstra, pero suele tocar muchos menos nodos.
A* es el buscador de rutas por defecto en motores de videojuegos, robots de almacén y navegación de drones, y guía la planificación de rutas donde la distancia en línea recta sirve como cota inferior. También resuelve puzles deslizantes y otras búsquedas en espacios de estados con una buena heurística. En entrevistas es la continuación natural de Dijkstra: suelen preguntar qué propiedad necesita la heurística para que la respuesta siga siendo óptima.
A* es Dijkstra con un término extra. Donde Dijkstra siempre expande el nodo de menor coste confirmado g, A* expande el de menor f = g + h, donde h estima el coste que queda. Pon h a cero en todas partes y el pseudocódigo de abajo se convierte exactamente en Dijkstra.
A*(grafo, inicio, meta, h):
para cada vértice v: g[v] = infinito
g[inicio] = 0
f[inicio] = h(inicio)
abiertos = cola de prioridad con (f[inicio], inicio)
cerrados = conjunto vacío
mientras abiertos no esté vacío:
u = abiertos.extraerMin() // f más pequeño
si u == meta: devolver reconstruir(u)
añadir u a cerrados
para cada arista (u, v, w):
si v en cerrados: continuar
tentativo = g[u] + w
si tentativo < g[v]:
previo[v] = u
g[v] = tentativo
f[v] = tentativo + h(v)
abiertos.insertar(f[v], v)
devolver sin caminoSaltar los nodos cerrados en la línea 15 solo es seguro cuando h es consistente, es decir h(u) <= w(u, v) + h(v) para toda arista. Con una heurística meramente admisible hay que permitir que los nodos salgan del conjunto cerrado y se reabran, o A* puede devolver un camino subóptimo. El visualizador de arriba escala la distancia en línea recta por el coste por unidad más barato que ofrece cualquier arista, lo que hace h consistente por la desigualdad triangular, así que no hace falta reabrir nada.
Cinco nodos, con S en el origen y la meta G cinco unidades a su derecha. Lo interesante de la traza es el nodo que A* nunca toca.
Grafo de ejemplo: S(0,0), A(2,1), B(2,-1), C(1,4), G(5,0). Aristas S-A = 3, S-B = 2, S-C = 4, A-B = 2, A-G = 4, B-G = 6, C-G = 7. Escalando la distancia en línea recta por el coste por unidad más barato de cualquier arista (0,894) resulta h(S) = 4,47, h(A) = 2,83, h(B) = 2,83, h(C) = 5,06, h(G) = 0.
A* devuelve S a A a G con coste 7, habiendo expandido 4 nodos. Dijkstra sobre el mismo grafo devuelve el mismo camino con el mismo coste, pero expande 5: procesa C antes de estar dispuesto a fijar la meta. C nunca mereció una visita, y h es lo que permitió a A* saberlo sin comprobarlo.
Tiempo: O((V + E) log V) en el peor caso con un montículo binario · Espacio: O(V)
El peor caso es el de Dijkstra, y por el mismo motivo: cada vértice puede entrar una vez en la cola de prioridad y cada arista puede disparar una operación de decremento de clave, lo que da V extracciones y E actualizaciones a O(log V) cada una. La heurística no cambia esa cota. Lo que cambia es la constante: los nodos cuyo f supera el coste final de la meta no se expanden nunca. Con h = 0 A* degenera exactamente en Dijkstra; con un h perfecto recorre directamente el camino óptimo. Sobre 4000 grafos ponderados generados al azar, la implementación de arriba expandió 4,58 nodos de media frente a los 5,52 de Dijkstra, y devolvió el coste óptimo todas las veces.
A* solo compensa su maquinaria extra cuando tienes una meta y una estimación utilizable de la distancia hasta ella. Sin alguna de las dos, una de estas opciones es la mejor herramienta.
| Alternativa | Prefiérela cuando | Coste |
|---|---|---|
| Algoritmo de Dijkstra | Necesitas caminos mínimos a todos los nodos, o no tienes ninguna heurística con sentido. A* con h = 0 es exactamente esto. | O((V + E) log V) |
| Búsqueda en anchura | Todas las aristas cuestan lo mismo. BFS encuentra la misma respuesta sin cola de prioridad alguna. | O(V + E) |
| Bellman-Ford | Algunos pesos son negativos. A* hereda de Dijkstra la suposición de pesos no negativos y falla aquí. | O(V * E) |
| A* bidireccional | Grafos muy grandes con un único origen y una única meta. Buscar desde ambos extremos reduce aproximadamente a la mitad la región explorada. | O((V + E) log V) |
| A* ponderado (f = g + w*h) | Cambias optimalidad por velocidad. Con w > 1 encuentra caminos antes, pero solo garantiza costes dentro de un factor w del óptimo. | O((V + E) log V) |
Leer el artículo completo: A* Search Algorithm: Step-by-Step Guide
Leer el artículo completo: A* Search Algorithm in AI: The Complete Guide
Algoritmos relacionados: Algoritmo de Dijkstra, Búsqueda en Anchura, Algoritmo de Bellman-Ford