Innanzitutto, un piccolo background per indicare perché questo non è un problema difficile. Il flusso attraverso un fiume garantisce che i suoi segmenti, se correttamente digitalizzati, possono sempre essere orientati per formare un grafico aciclico diretto (DAG). A sua volta, un grafico può essere ordinato in modo lineare se e solo se è un DAG, usando una tecnica nota come ordinamento topologico . L'ordinamento topologico è veloce: i suoi requisiti di tempo e spazio sono entrambi O (| E | + | V |) (E = numero di spigoli, V = numero di vertici), il che è buono come si arriva. La creazione di un ordinamento così lineare semplificherebbe la ricerca del flusso principale.
Ecco quindi uno schizzo di un algoritmo . La bocca del torrente giace lungo il suo letto principale. Spostati a monte lungo ciascun ramo attaccato alla bocca (potrebbe essercene più di uno, se la bocca è una confluenza) e trova ricorsivamente il letto principale che porta a quel ramo. Seleziona il ramo per il quale la lunghezza totale è massima: questo è il tuo "backlink" lungo il letto principale.
Per chiarire questo, offro alcuni pseudocodici (non testati) . L'input è un insieme di segmenti di linea (o archi) S (comprendente il flusso digitalizzato), ciascuno con due punti finali distinti inizio (S) e fine (S) e una lunghezza positiva, lunghezza (S); e la foce p , che è un punto. L'output è una sequenza di segmenti che uniscono la bocca con il punto a monte più distante.
Dovremo lavorare con "segmenti contrassegnati" (S, p). Questi sono costituiti da uno dei segmenti S insieme a uno dei suoi due punti finali, p . Dovremo trovare tutti i segmenti S che condividono un endpoint con un punto sonda q , contrassegnare quei segmenti con i loro altri endpoint e restituire il set:
Procedure Extract(q: point, A: set of segments): Set of marked segments.
Quando non è possibile trovare tale segmento, Estrai deve restituire il set vuoto. Come effetto collaterale, Estrai deve rimuovere tutti i segmenti che sta ritornando dall'insieme A, modificando così A stesso.
Non sto dando un'implementazione di Extract: il tuo GIS fornirà la possibilità di selezionare segmenti S che condividono un endpoint con q . Contrassegnarli è semplicemente una questione di confronto tra inizio (S) e fine (S) con q e ritorno di uno qualsiasi dei due punti finali non corrispondenti.
Ora siamo pronti a risolvere il problema.
Procedure LongestUpstreamReach(p: point, A: set of segments): (Array of segments, length)
A0 = A // Optional: preserves A
C = Extract(p, A0) // Removes found segments from the set A0!
L = 0; B = empty array
For each (S,q) in C: // Loop over the segments meeting point p
(B0, M) = LongestUpstreamReach(q, A0)
If (length(S) + M > L) then
B = append(S, B0)
L = length(S) + M
End if
End for
Return (B, L)
End LongestUpstreamReach
La procedura "append (S, B0)" attacca il segmento S alla fine dell'array B0 e restituisce il nuovo array.
(Se il flusso è davvero un albero: niente isole, laghi, trecce, ecc., Allora puoi rinunciare al passaggio della copia di A in A0 .)
Alla domanda originale viene data risposta formando l'unione dei segmenti restituiti da LongestUpstreamReach.
Per illustrare , consideriamo lo stream nella mappa originale. Supponiamo che sia digitalizzato come una raccolta di sette archi. L'arco a va dalla bocca al punto 0 (parte superiore della mappa, a destra nella figura in basso, che è ruotata) a monte della prima confluenza al punto 1. È un arco lungo, diciamo 8 unità lunghe. L'arco b si dirama a sinistra (nella mappa) ed è corto, lungo circa 2 unità. L'arco c si dirama a destra ed è lungo circa 4 unità, ecc. Lasciando "b", "d" e "f" denotano i rami del lato sinistro mentre procediamo dall'alto verso il basso sulla mappa e "a", "c", "e" e "g" gli altri rami e numerando i vertici da 0 a 7, possiamo rappresentare in modo astratto il grafico come la raccolta di archi
A = {a=(0,1), b=(1,2), c=(1,3), d=(3,4), e=(3,5), f=(5,6), g=(5,7)}
Suppongo che abbiano lunghezze 8, 2, 4, 1, 2, 2, 2 per una a g , rispettivamente. La bocca è vertice 0.
Il primo esempio è la chiamata a Extract (5, {f, g}). Restituisce il set di segmenti contrassegnati {(f, 6), (g, 7)}. Si noti che il vertice 5 è alla confluenza di archi f e g (i due archi nella parte inferiore della mappa) e che (f, 6) e (g, 7) contrassegnare ciascuno di questi archi con i loro monte punti finali.
Il prossimo esempio è la chiamata a LongestUpstreamReach (0, A). La prima azione che effettua è la chiamata a Estrai (0, A). Ciò restituisce un set contenente il segmento contrassegnato (a, 1) e rimuove il segmento a dall'insieme A0 , che ora è uguale a {b, c, d, e, f, g}. Esiste un'iterazione del ciclo, dove (S, q) = (a, 1). Durante questa iterazione viene effettuata una chiamata a LongestUpstreamReach (1, A0). Ricorsivamente, deve restituire la sequenza (g, e, c) o (f, e, c): entrambi sono ugualmente validi. La lunghezza (M) che restituisce è 4 + 2 + 2 = 8. (Nota che LongestUpstreamReach non modifica A0 .) Alla fine del ciclo, segmenta unè stato aggiunto al letto del flusso e la lunghezza è stata aumentata a 8 + 8 = 16. Pertanto il primo valore restituito è costituito dalla sequenza (g, e, c, a) o (f, e, c, a), con una lunghezza di 16 in entrambi i casi per il secondo valore restituito. Questo mostra come LongestUpstreamReach si sposta a monte della foce, selezionando ad ogni confluenza il ramo con la distanza più lunga ancora da percorrere e tiene traccia dei segmenti attraversati lungo il suo percorso.
Un'implementazione più efficiente è possibile quando ci sono molte trecce e isole, ma per la maggior parte degli scopi ci sarà uno sforzo sprecato se LongestUpstreamReach è implementato esattamente come mostrato, perché ad ogni confluenza non c'è sovrapposizione tra le ricerche nei vari rami: il calcolo il tempo (e la profondità dello stack) sarà direttamente proporzionale al numero totale di segmenti.