Come trovare il percorso più breve con i nodi wormhole?


25

esempio

Questo è un esempio di ciò che voglio fare tramite il codice. So che puoi usare la ricerca del punto di salto per passare facilmente dal nodo verde al nodo rosso senza problemi, o anche A *. Ma come si calcola questo con gli orditi.

Nell'immagine, puoi vedere che ci vogliono solo 8 mosse per passare dal nodo verde al nodo rosso quando prende il percorso blu. Il percorso blu sposta istantaneamente la tua posizione da un nodo viola a quello successivo. Lo spazio nel mezzo che costa 2 mosse è un punto tra due zone di curvatura che devi muovere per raggiungere.

È chiaramente più veloce prendere il percorso blu, dal momento che devi solo spostarti di metà (approssimativamente) fino al percorso giallo, ma come posso farlo programmaticamente?

Allo scopo di risolvere questo problema, supponiamo che ci siano più "orditi" viola intorno al grafico che puoi usare, E sappiamo esattamente dove si deformerà ogni punto viola e dove si trovano sul grafico.

Alcuni orditi viola sono bidirezionali, altri no, il che significa che a volte è possibile inserire un ordito solo da un lato, ma non tornare indietro dopo l'ordito.

Ho pensato alla soluzione e ho solo concluso che sarei stato in grado di calcolare il problema controllando la distanza da ciascun punto di curvatura (meno i punti unidirezionali) e la differenza tra quei punti e i punti vicini a loro .

Il programma dovrebbe capire in qualche modo che è più vantaggioso fare il secondo ordito, invece di camminare dal primo salto. Quindi, invece di spostare 6 punti, quindi deformare, quindi spostare i rimanenti 8 passi a piedi (che è anche più veloce del non usare affatto gli orditi), prenderebbe le 6 mosse, quindi le due mosse sul secondo ordito.

EDIT: mi sono reso conto che il percorso blu in realtà richiederà 12 mosse, anziché 8, ma la domanda rimane la stessa.


4
Il percorso blu non dovrebbe essere 12 (compresi i due per passare dall'ultimo viola al rosso)?
BlueRaja - Danny Pflughoeft,

5
Il blu è in realtà 12 mosse (7 + 3 + 2), no?
Daniel Jour,

Oops, incasinato, grazie ragazzi! @DanielJour and Blue
Jeff smith,

Il modo "corretto" di modellare le distanze sarebbe quello di utilizzare la topologia e modellarla come superficie dimensionale superiore. Mi chiedo se una risposta del genere sarebbe appropriata qui?
Geeky I

Risposte:


49

La maggior parte degli algoritmi di ricerca del percorso sono definiti in termini di grafici, non in termini di griglie. In un grafico, una connessione tra due nodi altrimenti distanti non è realmente un problema.

Tuttavia, devi prenderti cura della tua euristica. Con wormhole, la distanza minima tra due nodi non è più la distanza euclidea e la distanza non soddisfa la disuguaglianza del triangolo. Tali euristiche non sono ammissibili per A *. Pertanto non è possibile utilizzare A * facilmente.

Ovviamente funzioneranno comunque algoritmi per la ricerca di percorsi come Dijkstra che non usano l'euristica. Questo è più simile a una ricerca di ampiezza e selezionerà i tuoi wormhole senza sforzo aggiuntivo. Tuttavia, Dijkstra visiterà più nodi di A * con una buona euristica. (Dijkstra è equivalente ad A * con heuristic(x) = 0.)

Penso che A * funzionerà se usi un euristico che tratta tutti i wormhole in uscita come un wormhole direttamente sul bersaglio: l'euristico può sottovalutare la distanza, ma non deve mai sopravvalutarlo. Cioè l'euristica sarebbe:

def wormhole_heuristic(x):
  return min(euclidean_distance(x, g) for g in [goal, wormholes...])

Per un'euristica molto accurata, è possibile (ricorsivamente) aggiungere la distanza dall'endpoint del wormhole all'obiettivo o al wormhole successivo. Vale a dire come pre-calcolo è possibile eseguire la ricerca del percorso sul sottografo (totalmente connesso) contenente tutti i wormhole e l'obiettivo, in cui la distanza tra due nodi è la loro distanza euclidea. Ciò può essere utile se il numero di wormhole è molto inferiore al numero di celle raggiungibili sulla griglia. La nuova euristica sarebbe:

def wormhole_heuristic(x):
  direct = euclidean_distance(x, goal)
  via_wormhole = min(euclidean_distance(x, w) + wormhole_path_distance(w, goal) for w in wormholes)
  return min(direct, via_wormhole)

Come sottolinea @Caleth nei commenti, tutto ciò è molto sintonizzabile e possiamo migliorare la prima euristica wormhole senza fare un percorso completo attraverso la rete wormhole, aggiungendo la distanza tra l'ultima uscita wormhole e l'obiettivo. Poiché non sappiamo quale uscita wormhole verrà utilizzata per ultima e non dobbiamo sopravvalutare, dobbiamo assumere l'uscita più vicina all'obiettivo:

def wormhole_heuristic(x):
  direct = euclidean_distance(x, goal)
  to_next_wormhole = min(euclidean_distance(x, w) for w in wormholes)
  from_last_wormhole = min(euclidean_distance(w.exit, goal) for w in wormholes)
  via_wormhole = to_next_wormhole + from_last_wormhole
  return min(direct, via_wormhole)

10
Vale la pena notare che Dijkstra è solo A * condijkstra_heuristic(x) = 0
Caleth,

Non capisco cosa intendi per [* wormholes, goal], lo spiegheresti?
Jeff Smith,

1
"La distanza euclidea alla più vicina uscita del wormhole" è una stima più economica wormhole_path_distancerispetto alla ricerca del sottografo e meno sottostimata di "tutte le uscite sono in porta".
Caleth,

3
@Caleth certamente! C'è un grande potenziale di messa a punto qui, ad esempio potremmo decidere di guardare avanti n = 3 salti. La ricerca del sottografo corrisponde alla chiusura di tutti i salti di wormhole aciclici. Il tuo suggerimento di guardare avanti n = 1 salti è molto elegante in quanto ha praticamente zero costi aggiuntivi :)
amon

1
Per la scossa della semplicità, supponiamo che ci sia un solo wormhole (due nodi), quindi puoi convertire questo piano 1-wormhole in 2 piani speculari, copiando un piano simmetrico con la linea equidistante tra questi due punti come asse di simmetria. Ora hai due piani, chiamiamoli il piano reale (non prendi il wormhole) e il piano immaginario (hai preso il wormhole). Ora introduciamo la coordinata Z. Questa coordinata sarà 0 per ogni punto del piano reale e sarà dist (wormhole, punto) per ogni punto del piano immaginario. Successivamente, applica A * per lo spazio tridimensionale.
Lilezek,

5

Hai un grafico con 6 vertici su una griglia con coordinate:

A ( 0,0)
B ( 4,7)
C ( 7,4)
D (10,4)
E (16,2)
F (16,0)

È possibile generare un grafico completo su quei vertici e assegnare un costo a ciascun bordo in cui il costo è MAX( ABS( x1 - x2 ), ABS( y1 - y2 ) )per i bordi standard e un costo di 0 per i wormhole.

Questo ti darà i costi (come matrice di adiacenza):

   A  B  C  D  E  F
- -- -- -- -- -- --
A  -  7  7 10 16 16
B  7  -  0  6 12 12
C  7  0  -  3  9  9
D 10  6  3  -  0  6
E 16 12  9  0  -  2
F 16 12  9  6  2  -

Se ci sono orditi unidirezionali, crea solo bordi nel grafico (o matrice di adiacenza) che vanno in quella direzione ma non nel contrario.

È quindi possibile utilizzare l'algoritmo di Dijkstra con una coda prioritaria .

Inizia da Ae spingi ciascun bordo adiacente nella coda prioritaria:

Formato: (percorso: costo)

queue     = [ (A-B : 7), (A-C : 7), (A-D : 10), (A-E : 16), (A-F : 16) ]

Man mano che gli elementi vengono inseriti nella coda, tenere traccia del costo minimo per ciascun vertice e inserire i percorsi nella coda solo se è inferiore al costo minimo esistente.

min-costs = { A: 0, B: 7, C: 7, D: 10, E: 16, F: 16 }

Rimuovere il primo elemento dalla coda e, se il suo costo corrisponde ancora al costo minimo, reinserire tutti i percorsi compositi formati da quel percorso e i bordi adiacenti nella coda prioritaria (se i percorsi compositi hanno un costo inferiore rispetto al minimo esistente):

Rimuovere: (A-B : 7)

  • Prova (A-B-A : 14)- rifiuta come costo più elevato
  • Prova (A-B-C : 7): rifiuta lo stesso costo
  • Prova (A-B-D : 13)- rifiuta come costo più elevato
  • Prova (A-B-E : 19)- rifiuta come costo più elevato
  • Prova (A-B-F : 19)- rifiuta come costo più elevato

Rimuovere (A-C : 7)

  • Prova (A-C-A : 14)- rifiuta come costo più elevato
  • Prova (A-C-B : 7): rifiuta lo stesso costo
  • Prova (A-C-D : 10): rifiuta lo stesso costo
  • Prova (A-C-E : 16): rifiuta lo stesso costo
  • Prova (A-C-F : 16): rifiuta lo stesso costo

Rimuovere (A-D : 10)

  • Prova (A-D-A : 20)- rifiuta come costo più elevato
  • Prova (A-D-B : 16)- rifiuta come costo più elevato
  • Prova (A-D-C : 13)- rifiuta come costo più elevato
  • Prova (A-D-E : 10): inserisci in coda
  • Prova (A-D-F : 16): rifiuta lo stesso costo

Ora la coda sarà simile a:

queue     = [ (A-D-E : 10), (A-E : 16), (A-F : 16) ]
min-costs = { A: 0, B: 7, C: 7, D: 10, E: 10, F: 16 }

Rimuovere (A-D-E : 10)

  • Prova (A-D-E-A : 26)- rifiuta come costo più elevato
  • Prova (A-D-E-B : 22)- rifiuta come costo più elevato
  • Prova (A-D-E-C : 19)- rifiuta come costo più elevato
  • Prova (A-D-E-D : 10): rifiuta lo stesso costo
  • Prova (A-D-E-F : 12): inserisci in coda

Quindi la coda è:

queue     = [ (A-D-E-F : 12), (A-E : 16), (A-F : 16) ]
min-costs = { A: 0, B: 7, C: 7, D: 10, E: 10, F: 12 }

Rimuovi (A-D-E-F : 12), scopri che sei arrivato al nodo di destinazione con un costo di 12.

Nota: i percorsi (A-B-C-D-E-F), (A-C-D-E-F)e (A-D-E-F)tutti hanno lo stesso costo minimo di 12.


0

Crea una matrice contenente tutti i vertici e usa l'algoritmo Floyd-Wallenstein o l'algoritmo Bellman-Ford. Entrambi si tradurranno in una matrice con i percorsi più brevi possibili tra tutti i punti. È quindi possibile scorrere la matrice per trovare il percorso più breve che collega due punti. (il tuo problema è lo stesso di un TSP asimmetrico).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.