web-dev-qa-db-ja.com

隣接行列のサイクルの検出

AをグラフG = (V,E)の隣接行列とします。ノードijがエッジに接続されている場合はA(i,j) = 1、それ以外の場合はA(i,j) = 0

私の目的は、Gが非周期的であるかどうかを理解することです。サイクルは次のように定義されます。

  • ijが接続されています:A(i,j) = 1
  • jkが接続されています:A(j,k) = 1
  • kiが接続されています:A(k,i) = 1

私は次のようにマトリックスをナビゲートするソリューションを実装しました:

  • エッジから開始(i,j)
  • Oから発信されるエッジのセットjを選択します。つまり、jA番目の行のすべての1を選択します。
  • DFS方式でOをナビゲートします
  • このナビゲーションから生成されたパスの1つがノードiにつながる場合、サイクルが検出されます

マトリックス内のすべてのパスを評価する必要があるため、このソリューションは明らかに非常に低速です。 Aが非常に大きい場合、必要なオーバーヘッドは非常に大きくなります。 DFSなどの高価なアルゴリズムを使用せずにサイクルを見つけるために隣接行列をナビゲートする方法があるかどうか疑問に思いました。

ソリューションをMATLABに実装したいと思います。

前もって感謝します、

エレノア。

13
Eleanore

Danil の観察に基づいて、A^nを計算する必要があります。これを行うには、もう少し効率的な方法があります。

n = size(A,1);
An = A; 
for ii = 2:n
     An = An * A; % do not re-compute A^n from skratch
     if trace(An) ~= 0
        fprintf(1, 'got cycles\n');
     end
end
7
Shai

この math.stackexchange の質問に答えるときに、この質問に出くわしました。将来の読者のために、Danil Asotskyの答えが間違っていることを(他の人がすでに述べているように)指摘し、代替アプローチを提供する必要があると感じています。 Danilが参照している定理は、A ^ kの(i、j)エントリは、iからjまでの長さkのウォークの数をカウントするというものです。 G.ここで重要なことは、walkが頂点を繰り返すことが許可されていることです。したがって、A ^ kの対角エントリが正の場合でも、エントリがカウントしている各ウォークには繰り返し頂点が含まれている可能性があるため、サイクルとしてカウントされません。

反例:長さ4のパスには、ダニルの答えによると4サイクルが含まれます(ハミルトン閉路問題を解決するため、答えはP = NPを意味することは言うまでもありません)。

とにかく、ここに別のアプローチがあります。グラフが非循環であるのは、それがフォレストである場合、つまり、c個のコンポーネントと正確にn-c個のエッジがある場合のみです。ここで、nは頂点の数です。幸い、 ラプラシアン行列 Lを使用してコンポーネントの数を計算する方法があります。これは、-Aの(i、i)エントリをエントリの合計に置き換えることによって取得されます。 Aの行i(つまり、iとラベル付けされた頂点の次数)。次に、Gの成分の数はn-rank(L)(つまり、Lの固有値としての0の多重度)であることがわかります。

したがって、エッジの数が少なくともn-(n-rank(L))+ 1である場合にのみ、Gにサイクルがあります。一方、握手補題により、エッジの数はtrace(L)のちょうど半分になります。そう:

0.5 * trace(L)= rank(L)の場合に限り、Gは非巡回です。同様に、Gは0.5 * trace(L)> = rank(L)+1の場合にのみサイクルを持ちます。

12
Casteels

Aが有向または無向グラフGの隣接行列である場合、行列_A^n_(つまり、Aのn個のコピーの行列積)には次のプロパティがあります。行iと列jのエントリは次の数を示します。 (有向または無向)頂点iから頂点jまでの長さnの歩行。

例えば。ある整数nの行列に対して、A ^ nにゼロ以外の対角要素が少なくとも1つ含まれている場合、グラフのサイズはnのサイクルになります。

行列の非ゼロの対角要素をチェックする最も簡単な方法は、行列trace(A) = sum(diag(A))を計算することです(この場合、行列パワーの要素は常に非負になります)。

Matlabのソリューションは次のとおりです。

_for n=2:size(A,1)
   if trace(A^n) ~= 0
      fprintf('Graph contain cycle of size %d', n)
      break;
   end
end
_
5
Danil Asotsky

このアプローチはDFSを使用しますが、後続のDFSでノードを繰り返さないため、非常に効率的です。

高レベルのアプローチ:

すべてのノードの値を_-1_に初期化します。

探索されていない各ノードからDFSを実行し、そのノードの値を_0_から始まる自動インクリメント値の値に設定します。

これらのDFSの場合、各ノードの値を_previous node's value + i/n^k_で更新します。ここで、そのノードは前のノードのi番目の子であり、kは探索された深さですすでに探索されたノードをスキップします(より大きな値をチェックすることを除く)。

したがって、_n = 10_の例:

_   0.1   0.11   0.111
   j   - k    - p
0 /    \ 0.12
i \ 0.2  l
    m

1   1.1
q - o
...
_

各ノードに_i/branching factor+1_を使用して数値の有効桁数を減らすこともできますが、それを決定するには追加の計算が必要です。

したがって、上記では、ijの2つの子を持つmからDFSを実行しました。 mには子がなく、jには2つの子があり、....次に、iで終了し、次の未探索ノードqから別のDFSを開始しました。

より大きな値に遭遇するときはいつでも、あなたはサイクルが起こったことを知っています。

複雑さ:

すべてのノードを最大で1回チェックし、すべてのノードでn回チェックするため、複雑さはO(n^2)です。これは、マトリックス内のすべてのエントリを1回確認するのと同じです(これ以上のことはできません)。より)。

注:

隣接リスト は、非常に密なグラフでない限り、おそらく隣接行列よりも高速であることに注意してください。

1
Dukeling

行列アプローチに関するもう少し考え...引用された例は、切断されたグラフの隣接行列です(ノード1と2が接続され、ノード3と4が接続されていますが、どちらのペアも他のペアに接続されていません)。 A ^ 2を計算するとき、答えは(述べたように)単位行列です。ただし、Trace(A ^ 2)= 4であるため、これは、それぞれ長さが2のループが2つあることを示しています(これは正しいです)。これらのループが適切に識別され、マトリックスから削除されるまで、A ^ 3の計算は許可されません。これは、いくつかのステップを必要とする複雑な手順であり、R.L。Norman、「有向グラフのサイクルの位置を特定するためのマトリックス法」、AIChE J、11-3(1965)pp.450-452によって詳細に説明されています。注意:このアプローチがALLサイクル、UNIQUEサイクル、および/またはELEMENTARYサイクルを検出することが保証されているかどうかは作成者からは不明です。私の経験では、それが一意のサイクルだけを確実に特定するわけではないことを示唆しています。

0
Don Elmore

それは私も見つけた問題です。説明は次のとおりだと思いました。
私たちがサイクルについて話すとき、暗黙のうちに私たちは有向サイクルを意味します。有向グラフを考えると、あなたが持っている隣接行列は異なる意味を持っています。それは確かに長さ2の有向サイクルです。したがって、$ A ^ n $の解は、実際には有向グラフ用です。無向グラフの場合、修正は、マトリックスの上三角バージョン(残りはゼロで埋められている)を検討し、手順を繰り返すことだと思います。これが正しい答えかどうか教えてください。

0
Pagol

有向グラフGがその隣接行列Mで表される場合、その中に閉路がある場合、M '=(I --M)は特異になります。 I:Mと同じ次数の単位行列

0
Saptarshi Ghosh