インエッジとアウトエッジを持つ大きな(約2,000万ノード)有向グラフがあります。グラフのどの部分が最も注目に値するかを知りたい。多くの場合、グラフのほとんどは退屈です、または少なくともそれはすでに十分に理解されています。 「注意」を定義する方法は、「接続性」の概念によるものです。つまり、グラフで最も接続されているノードを見つけるにはどうすればよいですか?
以下では、ノード自体にはスコアがなく、エッジには重みがなく、ノードが接続されているかどうかを仮定できます。
このWebサイトが推奨 n次元空間、固有ベクトル、グラフ中心性の概念、pageRankなどのかなり複雑な手順。この問題は複雑ですか?
グラフ全体の単純な幅優先トラバーサルを実行できませんか?各ノードで、エッジ内の数を見つける方法を見つけます。エッジが最も多いノードは、グラフで最も重要なノードです。ここで何か不足していますか?
次の図は、さまざまな測定値の違いの良い鍵であることがわかりました(無向グラフが描かれていますが、いくつかは有向グラフにも当てはまります)。学位の中心性[D]は単純です。ほとんどの出入りリンクを持っているのは誰ですか。固有ベクトル中心性[C]は、間接的な影響の概念をよりよく捉えます。定義と詳細については、ウィキペディアの Centrality を参照してください。
グラフが解決する実際の問題やグラフの詳細がわからない場合、これは関係がない可能性があります。しかし、あなたの基本的な解決策は2つの点で不正確であると私は思います。タイブレイクと私が「メガマニア」と呼ぶかもしれないものです。つまり、着信接続を数えるだけの場合、「同じ」重要性を持つノードに遭遇する可能性があります。しかし、私はその意図がより重要であるべきだと疑っています。また、場合によっては、ノード間で相互に高度に相互接続されているノードの「島」が存在することもありますが、大部分は緩く、ノードよりもはるかに重要に見えるノード(メガロマニア)が生成されます。
ソリューション?うーん...ページランクのようなもの、私は想像します!
1次の接続性(つまり、ノードに直接リンクしているエッジの数を数える)のみを計算するとします。
さらに、ノードの重みとすべてのエッジのリストを保存する場所があるすべてのノードのリストがすでにあると仮定します。
あなたは必要になるでしょう:
アルゴリズムは単純です。各Edgeを反復処理し、そのEdgeが参照するFromノードとToノードの重みを増やします。
「興味深い」ノードを見つけるための結果の解釈は、データセットに依存します。たとえば、着信と発信のエッジの比率が高い場合、Webページは興味深いかもしれませんが、空港とフライトのグラフは単に総トラフィックを見る場合があります。
どのノードがどのノードに接続するかが重要でない場合は、単純な2x2マトリックスを使用します。このマトリックスには、すべてのノードの列と1つのノードを表す行があります。接続されている各ノードは、列の値として1を取得します。次に、そのノードの接続数をカウントする列を各行の最後に追加します。これは前面に巨大なオーバーヘッドがありますが、最も接続されたノードを取得し、サイズが事前にわかっている場合はノードの挿入と削除が簡単になります。グラフのサイズが動的な場合、これは機能せず、静的なグラフに対してのみ機能します。配列を使用する必要があるため、メモリも問題になります。しかし、最も接続ノードを見つけることが大きな問題である場合、これはそれを行う簡単な方法です。
これを紙にスケッチするだけで、深さ優先の検索を選びました。どうして?さて、上記のアイデアを使用して、directedエッジをたどってグラフをトラバースすることにより、格納された整数をインクリメントして、特定のノードにアクセスした回数を理想的に追跡できます。幅優先の場合と同じくらい簡単にできます。
void search(node) {
nodeInitial = pick a node
for(nodeInitial:outgoingNodes as node) {
node:visits++ // this is how you track in-edges
if(node is unvisited) {
search(node)
node:visited = true
}
}
}
問題の一部は、有向グラフを使用すると、サブグラフ自体に接続されていても外部には接続されていない可能性があることです。グラフの他の部分がこれらのサブグラフに接続する場合がありますが、外部への接続がない場合があります。 3つの一方通行の上り専用道路がそこに向かって移動しているが、出て行く高速道路はない島(または島のグループ)を考えてみてください。私はこれがより大きな課題だと思います-訪問されていないグラフの部分を選択する方法。これを行うために私が考えることができる唯一の方法は、ノード自体が他のノードが指しているもの(受信ノード)も追跡している場合です。そこから、アルゴリズムを変更して、サブグラフ全体の完全なトラバーサルが行われた後に、着信ノードから新しいノードを選択する必要があります。次に、その別のサブグラフに飛び乗って、次のサブグラフにジャンプする必要があるまでトラバースします。
グラフをトラバースするときに更新される単一の個別のノードオブジェクトを作成するだけで、エッジが最も内側にある「最も重要な」ノードを格納するのは簡単だと思います。
Node mostImportant = null
void search(node) {
nodeInitial = pick a node
if(!mostImportant) {
mostImportant = nodeInitial
}
for(nodeInitial:outgoingNodes as node) {
node:visits++ // this is how you track in-edges
if(node.visits > mostImportant.visits)
mostImportant = node
if(node is unvisited) {
search(node)
node:visited = true
}
}
}
アルゴリズムは通常、どちらか一方を最適化しようとします。でもNP問題は解決可能ですが、すべての可能な組み合わせを試す必要があるため、計算コストが高くなります。解決しようとしている問題では、問題は何であるかということです時間、メモリ、または何か他のものを最適化しようとしていますか?
200万ノードであることを考えると、すべてが一度にメモリに読み込まれるのか、ディスクに常駐するのかが不明であり、メモリ管理とI/Oの最小化が要因になる可能性があります。
あなたのグラフ全体がメモリにあると仮定すると、問題は誰がグラフを作成するかです。作成中(またはディスクからの読み取り時)にグラフ内のノードのエッジの数を保持し、それらを順序付けられたリストに保持できますか?
もう1つは、グラフが実際の生活の中で2つ以上の完全に切り離された部分を含むことがあるということです(そのようなグラフを1つ処理する必要がありました)。そのような場合、すべてのノードを反復処理し(幅が先か深さが先かを問わず)、すべてのノードのすべてのエッジを数えると便利です。ほとんどの場合、現代の言語ではすべてのエッジがコレクションの一種であり、そのコレクションの長さまたはカウントを取得するためのプロパティまたはメソッドが既にあるため、すべてのエッジでの反復を節約できます)。
これらの両方のソリューションの複雑さは、O(n log n)になることがあります。ここで、nはnoです。ノードの。
時間とメモリはしばしば問題になりますが、時にはこれらの属性よりも単純さの方が重要であるため、私もそれを方程式に取り入れています。このような多くの状況では、何を実装するかを決定する前に、実装の複雑さを賛成/反対のリストのもう1つの要素と見なします。