Qual è la differenza tra la ricerca su grafo e le versioni di ricerca ad albero per quanto riguarda le ricerche DFS, A * nell'intelligenza artificiale ?
Qual è la differenza tra la ricerca su grafo e le versioni di ricerca ad albero per quanto riguarda le ricerche DFS, A * nell'intelligenza artificiale ?
Risposte:
A giudicare dalle risposte esistenti, sembra esserci molta confusione su questo concetto.
La distinzione tra ricerca ad albero e ricerca su grafo non è radicata nel fatto che il grafo del problema sia un albero o un grafo generale. Si presume sempre che tu abbia a che fare con un grafico generale. La distinzione risiede nel modello di attraversamento utilizzato per la ricerca nel grafico, che può essere a forma di grafico o ad albero.
Se hai a che fare con un problema a forma di albero , entrambe le varianti dell'algoritmo portano a risultati equivalenti. Quindi puoi scegliere la variante di ricerca dell'albero più semplice.
Il tuo algoritmo di ricerca del grafico di base è simile al seguente. Con un nodo iniziale start
, bordi diretti come successors
e una goal
specifica utilizzata nella condizione di ciclo. open
tiene in memoria i nodi, che sono attualmente allo studio, l' elenco aperto . Notare che il seguente pseudo codice non è corretto in ogni aspetto (2).
open <- []
next <- start
while next is not goal {
add all successors of next to open
next <- select one node from open
remove next from open
}
return next
A seconda di come si implementa select from open
, si ottengono diverse varianti degli algoritmi di ricerca, come la ricerca in profondità (DFS) (scegli l'elemento più recente), la ricerca in larghezza (BFS) (scegli l'elemento più vecchio) o la ricerca dei costi uniforme (scegli l'elemento con il costo del percorso più basso ), la popolare ricerca A-Star scegliendo il nodo con il costo più basso più il valore euristico e così via.
L'algoritmo sopra indicato è in realtà chiamato ricerca ad albero . Visiterà più volte uno stato del grafico del problema sottostante, se sono presenti più percorsi diretti per il rooting nello stato iniziale. È anche possibile visitare uno stato un numero infinito di volte se si trova su un loop diretto. Ma ogni visita corrisponde a un diverso nodo nell'albero generato dal nostro algoritmo di ricerca. Questa apparente inefficienza a volte è richiesta, come spiegato in seguito.
Come abbiamo visto, la ricerca ad albero può visitare uno stato più volte. E come tale esplorerà più volte il "sottostruttura" trovato dopo questo stato, il che può essere costoso. La ricerca del grafico risolve questo problema tenendo traccia di tutti gli stati visitati in un elenco chiuso . Se un nuovo successore trovato next
è già noto, non verrà inserito nell'elenco aperto:
open <- []
closed <- []
next <- start
while next is not goal {
add next to closed
add all successors of next to open, which are not in closed
remove next from open
next <- select from open
}
return next
Notiamo che la ricerca del grafico richiede più memoria, poiché tiene traccia di tutti gli stati visitati. Ciò può essere compensato dall'elenco aperto più piccolo, che si traduce in una migliore efficienza di ricerca.
Alcuni metodi di implementazione select
possono garantire di restituire soluzioni ottimali - cioè un percorso più breve o un percorso con un costo minimo (per grafici con costi attaccati ai bordi). Questo vale fondamentalmente ogni volta che i nodi vengono espansi in ordine di costo crescente o quando il costo è una costante positiva diversa da zero. Un algoritmo comune che implementa questo tipo di selezione è la ricerca dei costi uniforme o, se i costi dei passaggi sono identici, BFS o IDDFS . IDDFS evita il consumo aggressivo di memoria di BFS ed è generalmente consigliato per la ricerca non informata (nota anche come forza bruta) quando la dimensione del passo è costante.
Anche il (molto popolare) algoritmo di ricerca ad albero A * fornisce una soluzione ottimale se utilizzato con un'euristica ammissibile . L' algoritmo di ricerca con grafo A * , tuttavia, fornisce questa garanzia solo quando viene utilizzato con un'euristica coerente (o "monotona") (una condizione più forte dell'ammissibilità).
Per semplicità, il codice presentato non:
state
o node
è più adeguato per i vertici del grafo del problema sottostante , in contrasto con il grafo trasversale, dipende dal contesto. Ma l'utilizzo state
per il problema dei vertici del grafo e node
per il grafo di attraversamento potrebbe sicuramente migliorare la chiarezza della risposta. Proverò a riscriverlo presto. Grazie.
Un albero è un caso speciale di un grafico, quindi qualunque cosa funzioni per i grafici generali funziona per gli alberi. Un albero è un grafo in cui c'è esattamente un percorso tra ogni coppia di nodi. Ciò implica che non contiene alcun ciclo, come afferma una risposta precedente, ma un grafo diretto senza cicli (un DAG, grafo aciclico diretto) non è necessariamente un albero.
Tuttavia, se sai che il tuo grafico ha alcune restrizioni, ad esempio che è un albero o un DAG, di solito puoi trovare un algoritmo di ricerca più efficiente rispetto a un grafico senza restrizioni. Ad esempio, probabilmente non ha molto senso usare A *, o la sua controparte non euristica "algoritmo di Dijkstra", su un albero (dove c'è comunque un solo percorso da scegliere, che puoi trovare da DFS o BFS) o su un DAG (dove un percorso ottimale può essere trovato considerando i vertici nell'ordine ottenuto dall'ordinamento topologico).
Per quanto riguarda diretto vs non orientato, un grafo non orientato è un caso speciale di un grafo diretto, ovvero il caso che segue la regola “se c'è un arco (collegamento, transizione) da u a v c'è anche un arco da v a u .
Aggiornamento : nota che se ciò che ti interessa è il modello di attraversamento della ricerca piuttosto che la struttura del grafico stesso, questa non è la risposta. Vedi, ad esempio, la risposta di @ ziggystar.
L'unica differenza tra un grafico e un albero è il ciclo . Un grafico può contenere cicli, un albero no. Quindi, quando si implementa un algoritmo di ricerca su un albero, non è necessario considerare l'esistenza di cicli, ma quando si lavora con un grafo arbitrario, è necessario considerarli. Se non gestisci i cicli, l'algoritmo potrebbe finire in un ciclo infinito o in una ricorsione infinita.
Un altro punto su cui riflettere sono le proprietà direzionali del grafico con cui hai a che fare. Nella maggior parte dei casi abbiamo a che fare con alberi che rappresentano le relazioni genitore-figlio su ciascun bordo. Un DAG (grafico aciclico diretto) mostra anche caratteristiche simili. Ma i grafici bidirezionali sono diversi. Ciascun bordo in un grafico bidirezionale rappresenta due vicini. Quindi gli approcci algoritmici dovrebbero differire un po 'per questi due tipi di grafici.
GRAFICO CONTRO ALBERO
Ma nel caso di AI Graph-search vs Tree-search
La ricerca del grafico ha una buona proprietà che ogni volta che l'algoritmo esplora un nuovo nodo e lo contrassegna come visitato, "Indipendentemente dall'algoritmo utilizzato", l'algoritmo esplora tipicamente tutti gli altri nodi che sono raggiungibili dal nodo corrente.
Si consideri ad esempio il seguente grafo con 3 vertici AB e C, e si considerino i seguenti gli archi
AB, BC e CA, beh, c'è un ciclo da C ad A,
E quando si esegue DFS a partire da A, A genererà un nuovo stato B, B genererà un nuovo stato C, ma quando C viene esplorato l'algoritmo proverà a generare un nuovo stato A ma A è già visitato, quindi verrà ignorato. Freddo!
Ma per quanto riguarda gli alberi? bene l'algoritmo degli alberi non contrassegna il nodo visitato come visitato, ma gli alberi non hanno cicli, come farebbe un ciclo infinito?
Considera questo albero con 3 vertici e considera i seguenti bordi
A - B - C radicata in A, verso il basso. E supponiamo di utilizzare l'algoritmo DFS
A genererà un nuovo stato B, B genererà due stati A e C, perché gli alberi non hanno "Segna un nodo visitato se è esplorato" quindi forse l'algoritmo DFS esplorerà di nuovo A, generando così un nuovo stato B, quindi stiamo entrando in un ciclo infinito.
Ma hai notato qualcosa, stiamo lavorando su bordi non orientati, cioè c'è una connessione tra AB e BA. ovviamente questo non è un ciclo, perché il ciclo implica che i vertici devono essere> = 3 e tutti i vertici sono distinti tranne il primo e l'ultimo nodo.
ST A-> B-> A-> B-> A non è un ciclo perché viola la proprietà di ciclismo> = 3. Ma in effetti A-> B-> C-> A è un ciclo> = 3 nodi distinti Selezionato, il primo e l'ultimo nodo sono gli stessi controllati.
Considera nuovamente i bordi dell'albero, A-> B-> C-> B-> A, ovviamente non è un ciclo, perché ci sono due B, il che significa che non tutti i nodi sono distinti.
Infine potresti implementare un algoritmo di ricerca ad albero, per evitare di esplorare lo stesso nodo due volte. Ma questo ha delle conseguenze.
In parole semplici, l'albero non contiene cicli e dove può farlo il grafico. Quindi, quando eseguiamo una ricerca, dovremmo evitare cicli nei grafici in modo da non entrare in cicli infiniti.
Un altro aspetto è che l'albero ha in genere una sorta di ordinamento topologico o una proprietà come l'albero di ricerca binario che rende la ricerca così veloce e facile rispetto ai grafici.