幅優先検索が時間実行されると言うのはなぜですか?


9

グラフの幅優先検索(BFS)の実行時間はO(| V | + | E |)であると(Wikipediaなどで)よく言われます。ただし、接続されたグラフには| V | \ leq | E | +1があり、接続されていないグラフであっても、BFSは開始頂点を含むコンポーネントの外側の頂点を決して見ません。そのコンポーネントには最大で| E |が含まれます  エッジなので、最大で| E | +1個の頂点が含まれ、それらの頂点のみがアルゴリズムがアクセスします。G=(V,E)O(|V|+|E|)|V||E|+1|E||E|+1

これは、|V|+|E|2|E|+1であることを意味するので、実行時間はO(|E|)だけであると言ってみませんか?

これは、Disjkstraのアルゴリズムの実行時間に関する質問へのコメントで生じました。


開始頂点があると仮定するのはなぜですか?たとえば、最大一致問題のBFSは、hopcroft karpアルゴリズムで一致しないすべての頂点から始まります。この場合、指定されたグラフが多くの接続されたコンポーネントのフォレストである場合、エッジャーよりも頂点が多くなり、それらすべてにアクセスします
narek Bojikian

2
@narekBojikian BFSはさまざまな方法で使用できますが、スタンドアロンアルゴリズムとして提示すると、ほとんどの場合、開始頂点があります。
David Richerby

回答:


9

BFSは通常、次のように記述されます(Wikipediaから)。

 1  procedure BFS(G,start_v):
 2      let Q be a queue
 3      label start_v as discovered
 4      Q.enqueue(start_v)
 5      while Q is not empty
 6          v = Q.dequeue()
 7          if v is the goal:
 8              return v
 9          for all edges from v to w in G.adjacentEdges(v) do
10             if w is not labeled as discovered:
11                 label w as discovered
12                 w.parent = v
13                 Q.enqueue(w)

問題はやや微妙な問題です。3行目に隠れています。問題は、発見された頂点を格納するためにどのデータ構造を使用するかです。

最も簡単な解決策は、頂点ごとに1つのエントリを持つブール配列を使用することです。この場合、配列のすべての要素をに初期化する必要がありfalse、これには時間がかかります。これは、エッジがまったくない場合でもすべてのグラフに適用されるため、間の関係を想定できませんと 実行時間を取得します。Θ|V||V||E|O|V|+|E|

初期化時間を持つデータ構造を回避できますか?最初の試みは、リンクされたリストを使用することかもしれません。ただし、頂点が発見されたかどうかをテストする場合(行10)、以前のように一定の時間ではなく、訪問した頂点の数に比例した時間がかかります。これは、実行時間がになることを意味し、最悪の場合はさらに悪くなります。我々がいることを書き換えたくないこと(注さらに悪化だから:それは悪いなどのようになり、一方、)Θ|V|O|V||E|O|E|2|V|4|V||E||V|

動的にサイズ変更された配列を使用すると、リストをソートしたままにできるため、検索にかかる時間はだけですが、それでも実行時間はのみになり、これは標準よりもさらに悪いです。Oログ|V|O|E|ログ|V|

最後に、動的サイズのハッシュテーブルを使用できます。定数サイズテーブルから始めて、  半分いっぱいになるたびに2倍にします。つまり、テーブルの最終的なサイズは、アルゴリズムが終了する前に検出される頂点の数の最大で2倍であり、開始頂点のコンポーネントの外側には何も検出されないため、これは最大でなります。さらに、ハッシュテーブルをコピーして展開するために行われる作業の合計は、最大で。ハッシュテーブルへの参照と挿入は償却されるため、実際に実行時間を取得します。c|E|+1c+2c+4c++2|E|4|E| O1O|E|

したがって、は可能ですが、実際の実装でそれを実行したいですか?私はおそらくそうは思いません。入力グラフに多数の小さなコンポーネントがあると信じる理由がない限り、ハッシュテーブルを維持するオーバーヘッドにより、実行時間に顕著な定数要素が追加されます。ハッシュテーブルの拡張には時間がかかる可能性がありますルックアップでは、ハッシュ関数を計算し、平均して、テーブル内の複数のスロットを調べる必要があります。ハッシュテーブルのキャッシュパフォーマンスが低いと、実際のコンピューターでも問題が発生する可能性があります。標準配列実装のほとんどの場合、部分はの主要な項ですO|E|4|E|O|E|O|V|+|E| これを実行する実際的なコストを考えると、ハッシュテーブルを使用して支配的な用語を削除する価値はありません。


1
実際のハッシュテーブルはキャッシュパフォーマンスが低いと主張するには強すぎるかもしれません。連鎖(つまり、リンクされたリスト)で実装されている場合、私は同意します。ただし、メモリの連続的なチャンクとオープンアドレス指定を使用して実装した場合は、それほど多くありません。
Juho

本当に素晴らしい答えです!余談ですが、動的サイズのハッシュテーブルは、小さなコンポーネントが多数ある場合だけでなく、頂点のハッシュ値が適切な定数によって制限されており、これが頻繁に発生する場合にも、確かに良い選択です。いい返事!
Carlos LinaresLópez19年

1
デビッド、私は何年も前に同様の考えを持っていました。答えは歴史的な視点にあると思います。
kelalaka
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.