接続されたサブグラフを見つけるための単純な再帰プログラムがあります。このコードは、グラフ内の未訪問の各頂点からすべてのエッジを (再帰的に) トラバースし、その過程で訪問済みのエッジを「ingraph=False」でマークすることによって機能します。(グラフは常に無向無重みグラフです)。
私が抱えている問題は、大きなグラフ (最大 100,000 個の頂点のサブグラフ) の場合、python(-2.7) がセグメンテーション違反を返すことです。しかし、これは python-2.6 で問題なく機能しました (今でも機能しています)。
誰かが2つの間で何が変わったのかを説明できますか(または、まったく別のものかもしれません)? python-2.7 で修正する方法はありますか (これは、ある時点で python-3 に移行しても壊れないことが望ましいです)。それとも、非再帰的な方法で書き直す必要がありますか?
ありがとう!
ソースの更新は次のとおりです。非再帰的なソリューションについては、以下の更新 3 を参照してください。
def floodfill(g):
for v in g.vertices: v.ingraph = True
sys.setrecursionlimit(g.size)
subgraph = 1
for v in g.vertices:
if v.ingraph:
v.ingraph = False
v.subgraph = subgraph
g.floodfill_search(v,subgraph)
subgraph += 1
def floodfill_search(g,v,subgraph):
for n in v.neighbors:
if n.ingraph:
n.ingraph = False
n.subgraph = subgraph
g.floodfill_search(n,subgraph)
- - - アップデート - - - -
私は、3台の異なるコンピューターに対して、再帰制限を〜16,000、〜24,000、および〜28,000にする小さな再帰テストを作成しました。さらに、結果は1台のPCでも一定ではありません。テストを 2 回実行すると、制限がわずかに異なります。たとえば、2 番目の例では、23800 から 23819 の間の結果が見つかりました。
#!/usr/bin/python
import sys
def callme(i):
print(i)
i+=1
callme(i)
sys.setrecursionlimit(int(1e6))
callme(0)
Cで実装されたデフォルトの「スタック」がないことがわかる限り、どの「Cスタック」が参照されているかは実際にはわかりません。C++にはスタックがありますが、同じ制限はありません。次の C++ の例は正常に実行されます (少なくとも 1M プッシュまで)。
#include <iostream>
#include <stack>
using namespace std;
int main () {
stack<int> s;
for(int i=0;i<1000000;i++) {
cout << "push " << i << endl;
s.push(i);
}
}
次の C コードもさらに深くなります (約 10 倍、~262,000)。
#include "stdio.h"
void callme(i) {
printf("calling %d\n",i);
callme(++i);
}
int main() {
int i=0;
callme(i);
}
---- 更新 2 -----
わかりました、これは明らかにpythonの意図です。プログラマーに深い再帰を避けるように強制します。 http://neopythonic.blogspot.ch/2009/04/tail-recursion-elimination.html
いずれにせよ、繰り返し書き直したほうがいいと今は思います。しかしその後は、ブースト グラフ ライブラリのようなグラフ理論ライブラリを使用して、おそらく C++ で最初からやり直すことになるでしょう。とにかく書き直さなければならない場合は、適切に書き直したほうがよいでしょう。
それにもかかわらず、これらの特定のサイズでなぜこれが起こるのかを理解するために、コメントをいただければ幸いです.
---- 更新 3 -----
これは、少なくとも迅速で汚い python の書き直しです。最後の行のせいでO(N^2)なので汚い。訪問されていない頂点のリストを追跡することにより、より優れた O(N) ソリューションが存在するはずですが、これは私のアプリケーションでは機能します。
def floodfill_nonrecursive(g):
for v in g.vertices: v.ingraph = True
start = g.vertices
subg = 1
while start:
q = [start[0]]
while q:
v = q.pop()
v.ingraph = False
v.subgraph = subg
for n in v.neighbors:
if n.ingraph:
n.ingraph = False
q.append(n)
subg += 1
start = [v for v in g.vertices if v.ingraph]