まず、これが難しい問題ではない理由を示すための小さな背景。川の流れにより、そのセグメントが正しくデジタル化されていれば、常に有向非循環グラフ(DAG)を形成できるようになります。次に、トポロジーソートとして知られる手法を使用して、DAGである場合にのみ、グラフを線形に順序付けることができます。トポロジカルソートは高速です。時間とスペースの要件は両方ともO(| E | + | V |)(E =エッジの数、V =頂点の数)で、これは得られるものと同じです。このような線形の順序を作成すると、主要な河川床を簡単に見つけることができます。
ここに、アルゴリズムのスケッチがあります。小川の口はその大きなベッドに沿っています。口に取り付けられた各枝に沿って上流に移動し(口が合流点である場合は複数ある場合があります)、その枝につながる主要なベッドを再帰的に見つけます。合計の長さが最も長いブランチを選択します。これは、主要なベッドに沿った「バックリンク」です。
これを明確にするために、いくつかの(テストされていない)擬似コードを提供します。入力は一連のラインセグメント(またはアーク)S(デジタル化されたストリームを含む)であり、それぞれ2つの異なるエンドポイントstart(S)およびend(S)と正の長さlength(S)を持ちます。そして、河口pがポイントです。出力は、口を最も遠い上流ポイントと結合する一連のセグメントです。
「マークされたセグメント」(S、p)で作業する必要があります。これらは、セグメントSの 1つと、その2つのエンドポイントpの1つで構成されます。プローブポイントqとエンドポイントを共有するすべてのセグメントSを見つけ、それらのセグメントを他のエンドポイントとマークし、セットを返す必要があります。
Procedure Extract(q: point, A: set of segments): Set of marked segments.
そのようなセグメントが見つからない場合、Extractは空のセットを返す必要があります。 副作用として、 ExtractはセットAから返されるすべてのセグメントを削除する必要があり、それによりA自体が変更されます。
Extractの実装は提供していません:GISは、エンドポイントをqと共有するセグメントSを選択する機能を提供します。それらをマークするのは、単にstart(S)とend(S)の両方をqと比較し、2つのエンドポイントのどちらかが一致しないものを返すだけです。
これで問題を解決する準備ができました。
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
手順「を追加(S、B0)」棒セグメントSアレイの端部でB0と新しい配列を返します。
(ストリームが本当にツリーである場合:島、湖、三つ編みなどはありません。AをA0にコピーするステップを省くことができます。)
元の質問は、LongestUpstreamReachによって返されるセグメントの結合を形成することによって回答されます。
を説明するために、元のマップのストリームを考えてみましょう。7つのアークのコレクションとしてデジタル化されているとします。アーク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)}
aからgまでの長さがそれぞれ8、2、4、1、2、2、2であると仮定します。口は頂点0です。
最初の例は、Extract(5、{f、g})の呼び出しです。マークされたセグメントのセット{(f、6)、(g、7)}を返します。頂点5はアークfとg(マップの下部にある2つのアーク)の合流点にあり、(f、6)と(g、7)はこれらの各アークを上流の端点でマークしていることに注意してください。
次の例は、LongestUpstreamReach(0、A)の呼び出しです。最初に実行するアクションは、Extract(0、A)の呼び出しです。この戻り顕著セグメント(1)を含むセットと、それは、セグメント削除A組からA0今{B、C、D、E、F、G}と等しいです。ループの反復が1つあり、(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)のいずれかで構成されます。どちらの場合も、2番目の戻り値の長さは16です。これは、LongestUpstreamReachがどのように口から上流に移動するかを示し、各合流点でこれまでに最も長い距離のブランチを選択し、そのルートに沿って移動したセグメントを追跡します。
多くのブレードとアイランドがある場合、より効率的な実装が可能ですが、ほとんどの目的で、LongestUpstreamReachが示されているとおりに実装されている場合、各合流点でさまざまなブランチの検索にオーバーラップがないため、無駄な労力はほとんどありません:コンピューティング時間(およびスタックの深さ)は、セグメントの総数に直接比例します。