[英]Find all chordless cycles in an undirected graph
如何在無向圖中找到所有無弦循環 ?
例如,給出圖表
0 --- 1
| | \
| | \
4 --- 3 - 2
算法應該返回1-2-3和0-1-3-4,但絕不會返回0-1-2-3-4。
(注意: [1]這個問題與平面圖中的小周期發現不同,因為圖不一定是平面的。 [2]我已經閱讀了文章生成所有周期,無弦周期和哈密頓周期的原理排除,但我不明白他們在做什么:)。 [3]我已經嘗試過CYPATH,但程序只給出了計數,readme.txt中的算法EnumChordlessPath有很大的拼寫錯誤,而且C代碼很亂。 [4]我並不想找到任意一組基金會周期 。 循環基礎可以有和弦。)
為從1到n的節點分配編號。
選擇節點編號1.將其命名為“A”。
枚舉來自'A'的成對鏈接。
選一個。 讓我們用B小於C調用相鄰節點'B'和'C'。
如果連接了B和C,則輸出循環ABC,返回步驟3並選擇另一對。
如果B和C未連接:
重復,直到你用完向量。
對所有對重復步驟3-5。
刪除節點1以及指向它的所有鏈接。 選擇下一個節點並返回步驟2。
編輯:你可以取消一個嵌套循環。
這看起來很有效,可能有bug,但你應該明白這個想法:
void chordless_cycles(int* adjacency, int dim)
{
for(int i=0; i<dim-2; i++)
{
for(int j=i+1; j<dim-1; j++)
{
if(!adjacency[i+j*dim])
continue;
list<vector<int> > candidates;
for(int k=j+1; k<dim; k++)
{
if(!adjacency[i+k*dim])
continue;
if(adjacency[j+k*dim])
{
cout << i+1 << " " << j+1 << " " << k+1 << endl;
continue;
}
vector<int> v;
v.resize(3);
v[0]=j;
v[1]=i;
v[2]=k;
candidates.push_back(v);
}
while(!candidates.empty())
{
vector<int> v = candidates.front();
candidates.pop_front();
int k = v.back();
for(int m=i+1; m<dim; m++)
{
if(find(v.begin(), v.end(), m) != v.end())
continue;
if(!adjacency[m+k*dim])
continue;
bool chord = false;
int n;
for(n=1; n<v.size()-1; n++)
if(adjacency[m+v[n]*dim])
chord = true;
if(chord)
continue;
if(adjacency[m+j*dim])
{
for(n=0; n<v.size(); n++)
cout<<v[n]+1<<" ";
cout<<m+1<<endl;
continue;
}
vector<int> w = v;
w.push_back(m);
candidates.push_back(w);
}
}
}
}
}
@aioobe有一個觀點。 只需找到所有周期,然后排除非無弦的周期。 這可能效率太低,但可以在此過程中修剪搜索空間以降低效率低下。 這是一個通用算法:
void printChordlessCycles( ChordlessCycle path) {
System.out.println( path.toString() );
for( Node n : path.lastNode().neighbors() ) {
if( path.canAdd( n) ) {
path.add( n);
printChordlessCycles( path);
path.remove( n);
}
}
}
Graph g = loadGraph(...);
ChordlessCycle p = new ChordlessCycle();
for( Node n : g.getNodes()) {
p.add(n);
printChordlessCycles( p);
p.remove( n);
}
class ChordlessCycle {
private CountedSet<Node> connected_nodes;
private List<Node> path;
...
public void add( Node n) {
for( Node neighbor : n.getNeighbors() ) {
connected_nodes.increment( neighbor);
}
path.add( n);
}
public void remove( Node n) {
for( Node neighbor : n.getNeighbors() ) {
connected_nodes.decrement( neighbor);
}
path.remove( n);
}
public boolean canAdd( Node n) {
return (connected_nodes.getCount( n) == 0);
}
}
這個怎么樣。 首先,將問題減少到找到通過給定頂點A的所有無弦循環。一旦找到了所有這些,你就可以從圖中刪除A,然后用另一個點重復,直到沒有任何東西為止。
如何找到通過頂點A的所有無弦循環? 給定一個允許的頂點列表,將其減少到找到從B到A的所有無弦路徑,並搜索廣度優先或深度優先。 請注意,當迭代從B可到達的頂點(在一個步驟中)時,當您選擇其中一個頂點時,您必須從允許頂點列表中刪除所有其他頂點(當B = A時要特別小心,以免消除三個 - 路徑)。
只是一個想法:
假設您在示例圖上枚舉循環,並且從節點0開始。
如果你對每個給定的邊緣進行廣度優先搜索,例如0 - 1,則在1處達到一個分叉。然后,首先再次達到0的循環是無弦的,其余的不是並且可以被消除...至少我想是這樣的。
你能用這樣的方法嗎? 或者有一個反例嗎?
找到所有周期。
無弦循環的定義是一組點,其中這些點的子集循環不存在。 因此,一旦你有所有循環問題就是簡單地消除具有子集循環的循環。
為了提高效率,對於您找到的每個循環,循環遍歷所有現有循環並驗證它不是另一個循環的子集,反之亦然,如果是,則消除較大的循環。
除此之外,只有困難在於弄清楚如何編寫一個算法來確定一個集合是否是另一個集合的子集。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.