По-перше, невелике тло, яке вказує на те, чому це не важка проблема. Потік через річку гарантує, що її ділянки, якщо правильно оцифровані, завжди можуть бути орієнтовані на формування спрямованого ациклічного графіка (DAG). У свою чергу, графік можна лінійно упорядкувати тоді і лише тоді, коли це DAG, використовуючи техніку, відому як топологічний сорт . Топологічне сортування швидко: його вимоги до часу та простору - це O (| E | + | V |) (E = кількість ребер, V = кількість вершин), що є настільки ж хорошим, скільки отримується. Створення такого лінійного упорядкування дозволить легко знайти основний русло потоку.
Ось, тоді, ескіз алгоритму . Устя струмка лежить уздовж основного русла. Перемістіть вгору за течією кожної гілки, прикріпленої до гирла (їх може бути більше, якщо рот є впаданням) і рекурсивно знайдіть основне русло, що веде до цієї гілки. Виберіть гілку, для якої загальна довжина найбільша: це ваша "зворотна лінія" уздовж основного ложа.
Щоб зробити це зрозумілішим, я пропоную кілька (неперевірених) псевдокодів . Вхід - це набір відрізків ліній (або дуг) S (що включає оцифрований потік), кожен з яких має дві чіткі кінцеві точки початку (S) і кінця (S) та додатну довжину, довжину (S); і гирло річки p , що є точкою. Вихід - це послідовність сегментів, що об'єднують рот з найбільш віддаленою точкою вгору за течією.
Нам знадобиться працювати з "позначеними сегментами" (S, p). Вони складаються з одного з відрізків S разом з однією з двох його кінцевих точок, с . Нам потрібно буде знайти всі сегменти S, які ділять кінцеву точку з точкою зонда q , позначити ці сегменти іншими їх кінцевими точками та повернути набір:
Procedure Extract(q: point, A: set of segments): Set of marked segments.
Коли такого сегмента не знайти, Extract повинен повернути порожній набір. В якості побічного ефекту, екстракт необхідно видалити всі сегменти вона повертається з безлічі А, тим самим змінюючи А сам.
Я не даю реалізувати Витяг: ваш ГІС надасть можливість вибору сегментів S, що ділиться кінцевою точкою з q . Позначення їх є просто питанням порівняння і початкового (S), і кінця (S) з q, і повернення того, що з двох кінцевих точок не відповідає.
Тепер ми готові вирішити проблему.
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
Процедура "append (S, B0)" приклеює сегмент S в кінці масиву B0 і повертає новий масив.
(Якщо потік насправді є деревом: немає островів, озер, тасьм тощо), тоді ви можете відмовитися від кроку копіювання A в A0 .)
На вихідне запитання відповідає відповідь, утворюючи об'єднання сегментів, повернених LongestUpstreamReach.
Для ілюстрації розглянемо потік у вихідній карті. Припустимо, вона оцифрована як колекція із семи дуг. Дуга a йде від гирла в точці 0 (вгорі карти, праворуч на рисунку внизу, яка повертається) вгору за течією до першого впадіння в точці 1. Це довга дуга, скажімо, 8 одиниць. Дуга b відгалужується ліворуч (на карті) і коротка, довжиною близько 2 одиниць. Дуга c відгалужується праворуч і становить близько 4 одиниць і т.д. Дозволяючи "b", "d" і "f" позначають ліві гілки, коли ми рухаємося згори вниз на карті, і "a", "c", "e" та "g" інші гілки, і нумеруючи вершини від 0 до 7, ми можемо абстрактно представити графік як сукупність дуг
A = {a=(0,1), b=(1,2), c=(1,3), d=(3,4), e=(3,5), f=(5,6), g=(5,7)}
Я вважаю , що вони мають довжину 8, 2, 4, 1, 2, 2, 2 для через г , відповідно. Рот - вершина 0.
Перший приклад - заклик до Extract (5, {f, g}). Він повертає набір позначених сегментів {(f, 6), (g, 7)}. Зверніть увагу, що вершина 5 знаходиться в місці злиття дуг f і g (дві дуги внизу карти) і що (f, 6) і (g, 7) позначають кожну з цих дуг їх кінцевими точками вгору за течією .
Наступний приклад - виклик LongestUpstreamReach (0, A). Перша дія, яку він вживає, - це заклик до вилучення (0, A). Це повертає набір, що містить позначений сегмент (a, 1), і він вилучає сегмент a з множини A0 , який тепер дорівнює {b, c, d, e, f, g}. Є одна ітерація циклу, де (S, q) = (a, 1). Під час цієї ітерації здійснюється виклик LongestUpstreamReach (1, A0). Рекурсивно він повинен повертати або послідовність (g, e, c), або (f, e, c): обидва однаково дійсні. Довжина (M), яку він повертає, дорівнює 4 + 2 + 2 = 8. (Зверніть увагу, що LongestUpstreamReach не змінює A0 .) В кінці циклу сегментуйте aбуло додано до русла потоку, а довжина збільшена до 8 + 8 = 16. Отже, перше повернене значення складається або з послідовності (g, e, c, a), або (f, e, c, a), довжиною 16 в будь-якому випадку для другого повернутого значення. Це показує, як LongestUpstreamReach просто переміщується вгору за течією від гирла, вибираючи в кожному злитті гілку з найдовшою відстані, яку потрібно пройти, і відстежує сегменти, пройдені по її маршруту.
Більш ефективна реалізація можлива, коли існує багато косів та островів, але для більшості цілей буде витрачено мало зусиль, якщо LongestUpstreamReach буде виконано точно так, як показано, тому що при кожному злитті не відбувається перекриття серед пошукових запитів у різних галузях: обчислювальна техніка час (і глибина стека) буде прямо пропорційним загальній кількості сегментів.