मैं किसी दिए गए नोड से / से एक निर्देशित ग्राफ में सभी चक्रों को कैसे पा सकता हूं (इसे पुनरावृति करता हूं)?
उदाहरण के लिए, मुझे कुछ इस तरह चाहिए:
A->B->A
A->B->C->A
लेकिन नहीं: बी-> सी-> बी
मैं किसी दिए गए नोड से / से एक निर्देशित ग्राफ में सभी चक्रों को कैसे पा सकता हूं (इसे पुनरावृति करता हूं)?
उदाहरण के लिए, मुझे कुछ इस तरह चाहिए:
A->B->A
A->B->C->A
लेकिन नहीं: बी-> सी-> बी
जवाबों:
मुझे अपनी खोज में यह पृष्ठ मिला और चूंकि चक्र समान रूप से जुड़े घटकों के समान नहीं हैं, मैं खोज करता रहा और आखिरकार, मुझे एक कुशल एल्गोरिदम मिला, जो एक निर्देशित ग्राफ के सभी (प्राथमिक) चक्रों को सूचीबद्ध करता है। यह डोनाल्ड बी। जॉनसन का है और पेपर निम्न लिंक में पाया जा सकता है:
http://www.cs.tufts.edu/comp/150GA/homeworks/hw1/Johnson%2075.PDF
एक जावा कार्यान्वयन में पाया जा सकता है:
http://normalisiert.de/code/java/elementaryCycles.zip
जॉनसन के एल्गोरिथम का एक गणितीय प्रदर्शन यहां पाया जा सकता है , कार्यान्वयन को दाईं ओर से डाउनलोड किया जा सकता है ( "लेखक कोड डाउनलोड करें" )।
नोट: वास्तव में, इस समस्या के लिए कई एल्गोरिदम हैं। उनमें से कुछ इस लेख में सूचीबद्ध हैं:
http://dx.doi.org/10.1137/0205007
लेख के अनुसार, जॉनसन का एल्गोरिथ्म सबसे तेज़ है।
A->B->C->A
प्राथमिक भी नहीं है?
simple_cycle
नेटवर्कएक्स के रूप में लागू किया गया है।
बैकट्रैकिंग के साथ पहली खोज को यहां काम करना चाहिए। पहले आपने एक नोड का दौरा किया या नहीं, इस पर नज़र रखने के लिए बूलियन मानों की एक सरणी रखें। यदि आप जाने के लिए नए नोड्स से बाहर निकलते हैं (एक नोड को हिट किए बिना), तो बस पीछे जाएं और एक अलग शाखा का प्रयास करें।
यदि आपके पास ग्राफ़ का प्रतिनिधित्व करने के लिए आसन्न सूची है तो DFS को लागू करना आसान है। उदाहरण के लिए adj [A] = {B, C} इंगित करता है कि B और C A के बच्चे हैं।
उदाहरण के लिए, नीचे छद्म कोड। "प्रारंभ" वह नोड है जिसे आप शुरू करते हैं।
dfs(adj,node,visited):
if (visited[node]):
if (node == start):
"found a path"
return;
visited[node]=YES;
for child in adj[node]:
dfs(adj,child,visited)
visited[node]=NO;
स्टार्ट नोड के साथ उपरोक्त फ़ंक्शन को कॉल करें:
visited = {}
dfs(adj,start,visited)
if (node == start):
- node and start
पहली कॉल में क्या है
start
) से जुड़े सभी चक्रों को ढूंढता है । यह उस शीर्ष पर शुरू होता है और एक डीएफएस करता है जब तक कि यह फिर से उस शीर्ष पर वापस नहीं जाता है, तब यह जानता है कि यह एक चक्र मिला। लेकिन यह वास्तव में चक्रों का उत्पादन नहीं करता है, बस उनमें से एक गिनती (लेकिन इसे संशोधित करने के लिए ऐसा करना चाहिए कि इसके बजाय बहुत मुश्किल नहीं होना चाहिए)।
start
। आपको वास्तव में विज़िट किए गए ध्वज को साफ़ करने की आवश्यकता नहीं है क्योंकि प्रत्येक विज़िट किए गए ध्वज को साफ़ कर दिया जाएगा visited[node]=NO;
। लेकिन ध्यान रखें कि यदि आपके पास एक चक्र है A->B->C->A
, तो आप उस 3 बार का पता लगाएंगे , जैसा कि start
उन 3 में से कोई भी हो सकता है। इसे रोकने के लिए एक विचार यह है कि एक और दौरा किया गया सरणी जहां प्रत्येक नोड जो start
किसी बिंदु पर नोड हो गया है, और उसके बाद आप इन पर दोबारा गौर नहीं करते हैं।
सबसे पहले - आप वास्तव में सभी चक्रों को खोजने की कोशिश नहीं करना चाहते हैं क्योंकि यदि 1 है तो उन की अनंत संख्या है। उदाहरण के लिए ABA, ABABA आदि या 2 चक्रों को 8-जैसे चक्र आदि में शामिल करना संभव हो सकता है, आदि ... सार्थक दृष्टिकोण सभी तथाकथित सरल चक्रों की तलाश करना है - जो खुद को छोड़कर पार नहीं करते हैं प्रारंभ / अंत बिंदु में। फिर यदि आप चाहें तो सरल चक्रों के संयोजन उत्पन्न कर सकते हैं।
एक निर्देशित ग्राफ में सभी सरल चक्रों को खोजने के लिए आधारभूत एल्गोरिदम में से एक यह है: ग्राफ़ में सभी सरल रास्तों (जो स्वयं को पार नहीं करते हैं) की गहराई-पहला ट्रैवर्सल करें। हर बार जब मौजूदा नोड के ढेर पर एक उत्तराधिकारी होता है, तो एक सरल चक्र की खोज की जाती है। इसमें स्टैक पर तत्वों की पहचान उत्तराधिकारी के साथ शुरू होती है और स्टैक के शीर्ष के साथ समाप्त होती है। सभी सरल रास्तों की गहराई का पहला पता गहराई से पहली खोज के समान है लेकिन आप स्टॉप पॉइंट्स के रूप में स्टैक पर वर्तमान में मौजूद अन्य स्थानों पर गए नोड्स को चिह्नित / रिकॉर्ड नहीं करते हैं।
उपरोक्त ब्रूट फोर्स एल्गोरिथ्म बहुत ही अकुशल है और इसके अलावा यह चक्रों की कई प्रतियाँ उत्पन्न करता है। हालांकि यह कई व्यावहारिक एल्गोरिदम का शुरुआती बिंदु है जो प्रदर्शन में सुधार और चक्र दोहराव से बचने के लिए विभिन्न संवर्द्धन लागू करता है। मुझे कुछ समय पहले यह जानकर आश्चर्य हुआ था कि ये एल्गोरिदम पाठ्यपुस्तकों और वेब पर आसानी से उपलब्ध नहीं हैं। इसलिए मैंने कुछ शोध किया और एक खुले स्रोत जावा पुस्तकालय में अप्रत्यक्ष रेखांकन में चक्रों के लिए 4 ऐसे एल्गोरिदम और 1 एल्गोरिदम को लागू किया: http://code.google.com/p/niographs/ ।
BTW, जब से मैंने अप्रत्यक्ष रेखांकन का उल्लेख किया है: उन लोगों के लिए एल्गोरिथ्म अलग है। एक फैले हुए पेड़ का निर्माण करें और फिर हर किनारे जो पेड़ का हिस्सा नहीं है, पेड़ में कुछ किनारों के साथ मिलकर एक सरल चक्र बनाता है। इस तरह पाए गए चक्र एक तथाकथित चक्र आधार बनाते हैं। सभी सरल चक्रों को 2 या अधिक विशिष्ट आधार चक्रों के संयोजन से पाया जा सकता है। अधिक जानकारी के लिए इसे देखें: http://dspace.mit.edu/bitstream/handle/1721.1/68106/FTL_R_1982_07.pdf ।
jgrapht
किए जाते http://code.google.com/p/niographs/
हैं, उदाहरण के लिए github.com/jgrapht/jgrapht/wiki/DirectedGraphDemo
इस समस्या को हल करने के लिए मुझे जो सबसे आसान विकल्प मिला, वह पायथन लिब नाम का प्रयोग था networkx
।
यह इस सवाल का सबसे अच्छा जवाब में जॉनसन के एल्गोरिथ्म को लागू करता है, लेकिन यह निष्पादित करने के लिए काफी सरल बनाता है।
संक्षेप में आपको निम्नलिखित की आवश्यकता है:
import networkx as nx
import matplotlib.pyplot as plt
# Create Directed Graph
G=nx.DiGraph()
# Add a list of nodes:
G.add_nodes_from(["a","b","c","d","e"])
# Add a list of edges:
G.add_edges_from([("a","b"),("b","c"), ("c","a"), ("b","d"), ("d","e"), ("e","a")])
#Return a list of cycles described as a list o nodes
list(nx.simple_cycles(G))
उत्तर: [[ : ए ’,, बी’,, डी ’,] ई’], ['ए ’, c बी’, a सी ’]]
nx.DiGraph({'a': ['b'], 'b': ['c','d'], 'c': ['a'], 'd': ['e'], 'e':['a']})
स्पष्टीकरण देना:
मजबूत रूप से जुड़े हुए घटक सभी उपसमूहों को मिलेंगे जिनमें कम से कम एक चक्र होगा, ग्राफ में सभी संभव चक्र नहीं। उदाहरण के लिए यदि आप सभी दृढ़ता से जुड़े घटकों को लेते हैं और उनमें से प्रत्येक को एक नोड (यानी प्रति घटक एक नोड) में मिलाते हैं, तो आपको एक चक्र (डीएजी वास्तव में) के साथ एक पेड़ मिलेगा। प्रत्येक घटक (जो मूल रूप से कम से कम एक चक्र के साथ एक सबग्राफ है) में आंतरिक रूप से कई और संभावित चक्र शामिल हो सकते हैं, इसलिए एससीसी को सभी संभव चक्र नहीं मिलेंगे, इसमें सभी संभव समूह मिलेंगे जिनमें कम से कम एक चक्र हो, और यदि आप समूह उन्हें, तो ग्राफ में चक्र नहीं होगा।
एक ग्राफ में सभी सरल चक्रों को खोजने के लिए , जैसा कि दूसरों ने उल्लेख किया है, जॉनसन का एल्गोरिथ्म एक उम्मीदवार है।
मुझे एक बार साक्षात्कार प्रश्न के रूप में दिया गया था, मुझे संदेह है कि यह आपके साथ हुआ है और आप मदद के लिए यहां आ रहे हैं। समस्या को तीन प्रश्नों में तोड़ें और यह आसान हो जाता है।
समस्या 1) पुनरावृत्ति मार्ग परिणामों का एक तरीका प्रदान करने के लिए पुनरावृत्ति पैटर्न का उपयोग करें। अगले मार्ग को प्राप्त करने के लिए तर्क रखने के लिए एक अच्छी जगह शायद आपके पुनरावृत्त का "मूव" है। एक वैध मार्ग खोजने के लिए, यह आपकी डेटा संरचना पर निर्भर करता है। मेरे लिए यह वैध मार्ग संभावनाओं से भरा एक वर्ग तालिका था इसलिए मुझे एक स्रोत दिए गए वैध स्थलों को प्राप्त करने के लिए एक क्वेरी का निर्माण करना था।
समस्या 2) प्रत्येक नोड को पुश करें जैसा कि आप उन्हें एक संग्रह में पाते हैं जैसा कि आप उन्हें प्राप्त करते हैं, इसका मतलब है कि आप देख सकते हैं कि क्या आप उस बिंदु पर बहुत आसानी से "दोहरीकरण" कर रहे हैं जिस संग्रह को आप फ्लाई पर बना रहे हैं।
समस्या 3) यदि किसी भी बिंदु पर आप देखते हैं कि आप दोहरीकरण कर रहे हैं, तो आप संग्रह और "बैक अप" से चीजों को पॉप कर सकते हैं। फिर उस बिंदु से फिर से "आगे बढ़ने" का प्रयास करें।
हैक: यदि आप Sql Server 2008 का उपयोग कर रहे हैं, तो कुछ नई "पदानुक्रम" चीजें हैं जिनका उपयोग आप जल्दी से इसे हल करने के लिए कर सकते हैं यदि आप किसी पेड़ में अपना डेटा बनाते हैं।
बैक किनारों वाले डीएफएस-आधारित वेरिएंट वास्तव में चक्र पाएंगे, लेकिन कई मामलों में यह न्यूनतम चक्र नहीं होगा । सामान्य तौर पर DFS आपको झंडा देता है कि एक चक्र है लेकिन यह वास्तव में साइकिल खोजने के लिए पर्याप्त नहीं है। उदाहरण के लिए, दो किनारों को साझा करने वाले 5 अलग-अलग चक्रों की कल्पना करें। केवल डीएफएस (बैकट्रैकिंग वेरिएंट सहित) का उपयोग करके साइकिल की पहचान करने का कोई सरल तरीका नहीं है।
जॉनसन का एल्गोरिथ्म वास्तव में सभी अद्वितीय सरल चक्र देता है और इसमें अच्छा समय और स्थान की जटिलता होती है।
लेकिन अगर आप केवल मिनिमल चक्र ढूंढना चाहते हैं (जिसका अर्थ है कि अधिक हो सकता है तो एक चक्र किसी भी शीर्ष से गुजर रहा है और हम न्यूनतम लोगों को खोजने में रुचि रखते हैं) और आपका ग्राफ बहुत बड़ा नहीं है, तो आप नीचे दी गई सरल विधि का उपयोग करने का प्रयास कर सकते हैं। यह बहुत ही सरल है, लेकिन जॉनसन की तुलना में सरल है।
तो, में से एक पूरी तरह से न्यूनतम चक्र को खोजने के लिए सबसे आसान तरीका है फ्लोयड के एल्गोरिथ्म का उपयोग करने के सभी निकटता मैट्रिक्स का उपयोग कर कोने के बीच कम से कम पथ मिल रहा है। यह एल्गोरिथ्म जॉनसन के रूप में इष्टतम कहीं नहीं है, लेकिन यह इतना सरल है और इसका आंतरिक लूप इतना तंग है कि छोटे रेखांकन (<= 50-100 नोड्स) के लिए यह बिल्कुल इसका उपयोग करने के लिए समझ में आता है। यदि आप पैरेंट ट्रैकिंग और O (1) का उपयोग नहीं करते हैं तो समय जटिलता O (n ^ 3), अंतरिक्ष जटिलता O (n ^ 2) है। सबसे पहले आइए इस प्रश्न का उत्तर खोजते हैं कि क्या कोई चक्र है। एल्गोरिथ्म मृत-सरल है। नीचे स्काला में स्निपेट है।
val NO_EDGE = Integer.MAX_VALUE / 2
def shortestPath(weights: Array[Array[Int]]) = {
for (k <- weights.indices;
i <- weights.indices;
j <- weights.indices) {
val throughK = weights(i)(k) + weights(k)(j)
if (throughK < weights(i)(j)) {
weights(i)(j) = throughK
}
}
}
मूल रूप से यह एल्गोरिथम सभी युग्मों (इसलिए वज़न तर्क) के बीच सभी सबसे छोटे रास्तों को खोजने के लिए भारित-किनारे ग्राफ पर संचालित होता है। इसे सही ढंग से काम करने के लिए आपको 1 प्रदान करने की आवश्यकता है अगर नोड्स या NO_EDGE के बीच एक निर्देशित किनारा है अन्यथा। एल्गोरिथ्म निष्पादित होने के बाद, आप मुख्य विकर्ण की जांच कर सकते हैं, यदि मान कम हैं तो NO_EDGE इस नोड की तुलना में मूल्य के बराबर लंबाई के चक्र में भाग लेता है। एक ही चक्र के हर दूसरे नोड में समान मूल्य (मुख्य विकर्ण पर) होगा।
चक्र को फिर से बनाने के लिए हमें माता-पिता के ट्रैकिंग के साथ एल्गोरिदम के थोड़ा संशोधित संस्करण का उपयोग करने की आवश्यकता है।
def shortestPath(weights: Array[Array[Int]], parents: Array[Array[Int]]) = {
for (k <- weights.indices;
i <- weights.indices;
j <- weights.indices) {
val throughK = weights(i)(k) + weights(k)(j)
if (throughK < weights(i)(j)) {
parents(i)(j) = k
weights(i)(j) = throughK
}
}
}
माता-पिता मैट्रिक्स शुरू में एक किनारे सेल में स्रोत वर्टेक्स इंडेक्स होना चाहिए अगर वहाँ कोने और -1 के बीच एक बढ़त है अन्यथा। फ़ंक्शन रिटर्न के बाद, प्रत्येक किनारे के लिए आपके पास सबसे कम पथ के पेड़ में मूल नोड का संदर्भ होगा। और फिर वास्तविक चक्रों को पुनर्प्राप्त करना आसान है।
सभी में हम सभी न्यूनतम चक्र खोजने के लिए निम्नलिखित कार्यक्रम है
val NO_EDGE = Integer.MAX_VALUE / 2;
def shortestPathWithParentTracking(
weights: Array[Array[Int]],
parents: Array[Array[Int]]) = {
for (k <- weights.indices;
i <- weights.indices;
j <- weights.indices) {
val throughK = weights(i)(k) + weights(k)(j)
if (throughK < weights(i)(j)) {
parents(i)(j) = parents(i)(k)
weights(i)(j) = throughK
}
}
}
def recoverCycles(
cycleNodes: Seq[Int],
parents: Array[Array[Int]]): Set[Seq[Int]] = {
val res = new mutable.HashSet[Seq[Int]]()
for (node <- cycleNodes) {
var cycle = new mutable.ArrayBuffer[Int]()
cycle += node
var other = parents(node)(node)
do {
cycle += other
other = parents(other)(node)
} while(other != node)
res += cycle.sorted
}
res.toSet
}
और परिणाम का परीक्षण करने के लिए एक छोटी सी मुख्य विधि
def main(args: Array[String]): Unit = {
val n = 3
val weights = Array(Array(NO_EDGE, 1, NO_EDGE), Array(NO_EDGE, NO_EDGE, 1), Array(1, NO_EDGE, NO_EDGE))
val parents = Array(Array(-1, 1, -1), Array(-1, -1, 2), Array(0, -1, -1))
shortestPathWithParentTracking(weights, parents)
val cycleNodes = parents.indices.filter(i => parents(i)(i) < NO_EDGE)
val cycles: Set[Seq[Int]] = recoverCycles(cycleNodes, parents)
println("The following minimal cycle found:")
cycles.foreach(c => println(c.mkString))
println(s"Total: ${cycles.size} cycle found")
}
और आउटपुट है
The following minimal cycle found:
012
Total: 1 cycle found
अप्रत्यक्ष ग्राफ़ के मामले में , हाल ही में प्रकाशित एक पेपर ( अप्रत्यक्ष ग्राफ़ में साइकिल और सेंट-पथ की इष्टतम सूची ) एक asymptotically इष्टतम समाधान प्रदान करता है। आप इसे पढ़ सकते हैं यहाँ http://arxiv.org/abs/1205.2766 या यहाँ http://dl.acm.org/citation.cfm?id=2627951 मैं इसे आपके प्रश्न का उत्तर नहीं है पता है, लेकिन के शीर्षक के बाद से आपका प्रश्न दिशा का उल्लेख नहीं करता है, यह अभी भी Google खोज के लिए उपयोगी हो सकता है
नोड एक्स पर शुरू करें और सभी बच्चे नोड्स के लिए जांचें (यदि अप्रत्यक्ष हैं तो माता-पिता और बच्चे के नोड बराबर हैं)। उन बच्चों के नोड्स को X के बच्चे होने के रूप में चिह्नित करें। ऐसे किसी भी बच्चे के नोड ए से, यह ए, एक्स 'के बच्चे होने का निशान है, जहां एक्स' को 2 कदम दूर होने के रूप में चिह्नित किया गया है।)। यदि आप बाद में X को हिट करते हैं और इसे X '' का बच्चा होने के रूप में चिह्नित करते हैं, तो इसका मतलब है कि X 3 नोड चक्र में है। माता-पिता के लिए इसे वापस लेना आसान है (जैसा कि, एल्गोरिथ्म के पास इसके लिए कोई समर्थन नहीं है, ताकि आपको पता चले कि जो भी माता-पिता के पास X है)।
नोट: यदि ग्राफ़ अप्रत्यक्ष है या किसी भी द्विदिश किनारों है, तो यह एल्गोरिथ्म अधिक जटिल हो जाता है, यह मानते हुए कि आप एक चक्र के लिए दो बार एक ही किनारे को पार नहीं करना चाहते हैं।
यदि आप चाहते हैं कि एक ग्राफ में सभी प्राथमिक सर्किटों को ढूंढना है, तो आप 1970 के बाद से एक पेपर पर पाए गए JAMES C. TIERNAN द्वारा EC एल्गोरिथ्म का उपयोग कर सकते हैं।
बहुत मूल चुनाव आयोग एल्गोरिथ्म के रूप में मैं php में इसे लागू करने में कामयाब (उम्मीद नहीं कोई गलती है नीचे दिखाया गया है)। यदि कोई हो तो यह लूप भी पा सकता है। इस कार्यान्वयन में सर्किट (जो मूल को क्लोन करने की कोशिश करता है) गैर शून्य तत्व हैं। शून्य यहां गैर-अस्तित्व के लिए खड़ा है (जैसा कि हम जानते हैं कि अशक्त)।
इसके अलावा नीचे एक अन्य कार्यान्वयन है जो एल्गोरिथ्म को अधिक स्वतंत्र देता है, इसका मतलब है कि नोड्स नकारात्मक संख्याओं से कहीं भी शुरू हो सकते हैं, जैसे -4, -3, -2, .. आदि।
दोनों ही मामलों में यह आवश्यक है कि नोड्स अनुक्रमिक हों।
आप मूल कागज, अध्ययन करने के लिए आवश्यकता हो सकती है जेम्स सी Tiernan प्राथमिक सर्किट एल्गोरिथ्म
<?php
echo "<pre><br><br>";
$G = array(
1=>array(1,2,3),
2=>array(1,2,3),
3=>array(1,2,3)
);
define('N',key(array_slice($G, -1, 1, true)));
$P = array(1=>0,2=>0,3=>0,4=>0,5=>0);
$H = array(1=>$P, 2=>$P, 3=>$P, 4=>$P, 5=>$P );
$k = 1;
$P[$k] = key($G);
$Circ = array();
#[Path Extension]
EC2_Path_Extension:
foreach($G[$P[$k]] as $j => $child ){
if( $child>$P[1] and in_array($child, $P)===false and in_array($child, $H[$P[$k]])===false ){
$k++;
$P[$k] = $child;
goto EC2_Path_Extension;
} }
#[EC3 Circuit Confirmation]
if( in_array($P[1], $G[$P[$k]])===true ){//if PATH[1] is not child of PATH[current] then don't have a cycle
$Circ[] = $P;
}
#[EC4 Vertex Closure]
if($k===1){
goto EC5_Advance_Initial_Vertex;
}
//afou den ksana theoreitai einai asfales na svisoume
for( $m=1; $m<=N; $m++){//H[P[k], m] <- O, m = 1, 2, . . . , N
if( $H[$P[$k-1]][$m]===0 ){
$H[$P[$k-1]][$m]=$P[$k];
break(1);
}
}
for( $m=1; $m<=N; $m++ ){//H[P[k], m] <- O, m = 1, 2, . . . , N
$H[$P[$k]][$m]=0;
}
$P[$k]=0;
$k--;
goto EC2_Path_Extension;
#[EC5 Advance Initial Vertex]
EC5_Advance_Initial_Vertex:
if($P[1] === N){
goto EC6_Terminate;
}
$P[1]++;
$k=1;
$H=array(
1=>array(1=>0,2=>0,3=>0,4=>0,5=>0),
2=>array(1=>0,2=>0,3=>0,4=>0,5=>0),
3=>array(1=>0,2=>0,3=>0,4=>0,5=>0),
4=>array(1=>0,2=>0,3=>0,4=>0,5=>0),
5=>array(1=>0,2=>0,3=>0,4=>0,5=>0)
);
goto EC2_Path_Extension;
#[EC5 Advance Initial Vertex]
EC6_Terminate:
print_r($Circ);
?>
फिर यह अन्य कार्यान्वयन है, ग्राफ़ से अधिक स्वतंत्र, बिना गोटो और बिना सरणी मान के, इसके बजाय यह सरणी कुंजियों का उपयोग करता है, पथ, ग्राफ़ और सर्किट को सरणी कुंजियों के रूप में संग्रहीत किया जाता है (यदि आप चाहें तो सरणी मानों का उपयोग करें, बस आवश्यक परिवर्तन करें लाइनों)। उदाहरण का ग्राफ अपनी स्वतंत्रता दिखाने के लिए -4 से शुरू होता है।
<?php
$G = array(
-4=>array(-4=>true,-3=>true,-2=>true),
-3=>array(-4=>true,-3=>true,-2=>true),
-2=>array(-4=>true,-3=>true,-2=>true)
);
$C = array();
EC($G,$C);
echo "<pre>";
print_r($C);
function EC($G, &$C){
$CNST_not_closed = false; // this flag indicates no closure
$CNST_closed = true; // this flag indicates closure
// define the state where there is no closures for some node
$tmp_first_node = key($G); // first node = first key
$tmp_last_node = $tmp_first_node-1+count($G); // last node = last key
$CNST_closure_reset = array();
for($k=$tmp_first_node; $k<=$tmp_last_node; $k++){
$CNST_closure_reset[$k] = $CNST_not_closed;
}
// define the state where there is no closure for all nodes
for($k=$tmp_first_node; $k<=$tmp_last_node; $k++){
$H[$k] = $CNST_closure_reset; // Key in the closure arrays represent nodes
}
unset($tmp_first_node);
unset($tmp_last_node);
# Start algorithm
foreach($G as $init_node => $children){#[Jump to initial node set]
#[Initial Node Set]
$P = array(); // declare at starup, remove the old $init_node from path on loop
$P[$init_node]=true; // the first key in P is always the new initial node
$k=$init_node; // update the current node
// On loop H[old_init_node] is not cleared cause is never checked again
do{#Path 1,3,7,4 jump here to extend father 7
do{#Path from 1,3,8,5 became 2,4,8,5,6 jump here to extend child 6
$new_expansion = false;
foreach( $G[$k] as $child => $foo ){#Consider each child of 7 or 6
if( $child>$init_node and isset($P[$child])===false and $H[$k][$child]===$CNST_not_closed ){
$P[$child]=true; // add this child to the path
$k = $child; // update the current node
$new_expansion=true;// set the flag for expanding the child of k
break(1); // we are done, one child at a time
} } }while(($new_expansion===true));// Do while a new child has been added to the path
# If the first node is child of the last we have a circuit
if( isset($G[$k][$init_node])===true ){
$C[] = $P; // Leaving this out of closure will catch loops to
}
# Closure
if($k>$init_node){ //if k>init_node then alwaya count(P)>1, so proceed to closure
$new_expansion=true; // $new_expansion is never true, set true to expand father of k
unset($P[$k]); // remove k from path
end($P); $k_father = key($P); // get father of k
$H[$k_father][$k]=$CNST_closed; // mark k as closed
$H[$k] = $CNST_closure_reset; // reset k closure
$k = $k_father; // update k
} } while($new_expansion===true);//if we don't wnter the if block m has the old k$k_father_old = $k;
// Advance Initial Vertex Context
}//foreach initial
}//function
?>
मैंने चुनाव आयोग को सूचित किया और दस्तावेज दिया लेकिन दुर्भाग्य से यह दस्तावेज ग्रीक में है।
DAG में सभी चक्रों को खोजने में दो चरण (एल्गोरिदम) शामिल हैं।
दृढ़ता से जुड़े घटकों के सेट को खोजने के लिए पहला कदम टारजन के एल्गोरिथ्म का उपयोग करना है।
दूसरा चरण जुड़ा घटकों के भीतर चक्र (पथ) को खोजने के लिए है। मेरा सुझाव Hierholzer के एल्गोरिथ्म के संशोधित संस्करण का उपयोग करना है।
विचार यह है:
एक परीक्षण मामले के साथ जावा कार्यान्वयन का लिंक यहां दिया गया है:
http://stones333.blogspot.com/2013/12/find-cycles-in-directed-graph-dag.html
मैं निम्नलिखित एल्गोरिथ्म पर ठोकर खाई जो जॉनसन के एल्गोरिथ्म की तुलना में अधिक कुशल प्रतीत होता है (कम से कम बड़े ग्राफ़ के लिए)। मैं हालांकि टार्जन के एल्गोरिथ्म की तुलना में इसके प्रदर्शन के बारे में निश्चित नहीं हूं।
इसके अतिरिक्त, मैंने केवल इसे अभी तक त्रिकोणों के लिए जांचा। यदि रुचि है, तो कृपया नॉरिशिज चिबा और ताकाओ निशिज़की ( http://dx.doi.org/10.1137/0214017 ) द्वारा "आर्बरिटी और सबग्राफ लिस्टिंग एल्गोरिदम" देखें।
जावास्क्रिप्ट सेट लिंक सूचियों का उपयोग करते हुए जावास्क्रिप्ट समाधान। तेजी से चलने वाले समय के लिए सेट जंगलों को अपग्रेड किया जा सकता है।
var input = '5\nYYNNN\nYYYNN\nNYYNN\nNNNYN\nNNNNY'
console.log(input);
//above solution should be 3 because the components are
//{0,1,2}, because {0,1} and {1,2} therefore {0,1,2}
//{3}
//{4}
//MIT license, authored by Ling Qing Meng
//'4\nYYNN\nYYYN\nNYYN\nNNNY'
//Read Input, preformatting
var reformat = input.split(/\n/);
var N = reformat[0];
var adjMatrix = [];
for (var i = 1; i < reformat.length; i++) {
adjMatrix.push(reformat[i]);
}
//for (each person x from 1 to N) CREATE-SET(x)
var sets = [];
for (var i = 0; i < N; i++) {
var s = new LinkedList();
s.add(i);
sets.push(s);
}
//populate friend potentials using combinatorics, then filters
var people = [];
var friends = [];
for (var i = 0; i < N; i++) {
people.push(i);
}
var potentialFriends = k_combinations(people,2);
for (var i = 0; i < potentialFriends.length; i++){
if (isFriend(adjMatrix,potentialFriends[i]) === 'Y'){
friends.push(potentialFriends[i]);
}
}
//for (each pair of friends (x y) ) if (FIND-SET(x) != FIND-SET(y)) MERGE-SETS(x, y)
for (var i = 0; i < friends.length; i++) {
var x = friends[i][0];
var y = friends[i][1];
if (FindSet(x) != FindSet(y)) {
sets.push(MergeSet(x,y));
}
}
for (var i = 0; i < sets.length; i++) {
//sets[i].traverse();
}
console.log('How many distinct connected components?',sets.length);
//Linked List data structures neccesary for above to work
function Node(){
this.data = null;
this.next = null;
}
function LinkedList(){
this.head = null;
this.tail = null;
this.size = 0;
// Add node to the end
this.add = function(data){
var node = new Node();
node.data = data;
if (this.head == null){
this.head = node;
this.tail = node;
} else {
this.tail.next = node;
this.tail = node;
}
this.size++;
};
this.contains = function(data) {
if (this.head.data === data)
return this;
var next = this.head.next;
while (next !== null) {
if (next.data === data) {
return this;
}
next = next.next;
}
return null;
};
this.traverse = function() {
var current = this.head;
var toPrint = '';
while (current !== null) {
//callback.call(this, current); put callback as an argument to top function
toPrint += current.data.toString() + ' ';
current = current.next;
}
console.log('list data: ',toPrint);
}
this.merge = function(list) {
var current = this.head;
var next = current.next;
while (next !== null) {
current = next;
next = next.next;
}
current.next = list.head;
this.size += list.size;
return this;
};
this.reverse = function() {
if (this.head == null)
return;
if (this.head.next == null)
return;
var currentNode = this.head;
var nextNode = this.head.next;
var prevNode = this.head;
this.head.next = null;
while (nextNode != null) {
currentNode = nextNode;
nextNode = currentNode.next;
currentNode.next = prevNode;
prevNode = currentNode;
}
this.head = currentNode;
return this;
}
}
/**
* GENERAL HELPER FUNCTIONS
*/
function FindSet(x) {
for (var i = 0; i < sets.length; i++){
if (sets[i].contains(x) != null) {
return sets[i].contains(x);
}
}
return null;
}
function MergeSet(x,y) {
var listA,listB;
for (var i = 0; i < sets.length; i++){
if (sets[i].contains(x) != null) {
listA = sets[i].contains(x);
sets.splice(i,1);
}
}
for (var i = 0; i < sets.length; i++) {
if (sets[i].contains(y) != null) {
listB = sets[i].contains(y);
sets.splice(i,1);
}
}
var res = MergeLists(listA,listB);
return res;
}
function MergeLists(listA, listB) {
var listC = new LinkedList();
listA.merge(listB);
listC = listA;
return listC;
}
//access matrix by i,j -> returns 'Y' or 'N'
function isFriend(matrix, pair){
return matrix[pair[0]].charAt(pair[1]);
}
function k_combinations(set, k) {
var i, j, combs, head, tailcombs;
if (k > set.length || k <= 0) {
return [];
}
if (k == set.length) {
return [set];
}
if (k == 1) {
combs = [];
for (i = 0; i < set.length; i++) {
combs.push([set[i]]);
}
return combs;
}
// Assert {1 < k < set.length}
combs = [];
for (i = 0; i < set.length - k + 1; i++) {
head = set.slice(i, i+1);
tailcombs = k_combinations(set.slice(i + 1), k - 1);
for (j = 0; j < tailcombs.length; j++) {
combs.push(head.concat(tailcombs[j]));
}
}
return combs;
}
शुरू नोड एस से डीएफएस, ट्रैवर्सल के दौरान डीएफएस पथ का ट्रैक रखें, और पथ को रिकॉर्ड करें यदि आप नोड वी से एस के रास्ते में बढ़त पाते हैं। (v, s) डीएफएस पेड़ में एक बैक-एज है और इस तरह एक चक्र होता है जिसमें s होता है।
क्रमचय चक्र के बारे में आपके प्रश्न के बारे में , यहाँ और अधिक पढ़ें: https://www.codechef.com/problems/PCYCLE
आप इस कोड को आज़मा सकते हैं (आकार और अंक संख्या दर्ज करें):
# include<cstdio>
using namespace std;
int main()
{
int n;
scanf("%d",&n);
int num[1000];
int visited[1000]={0};
int vindex[2000];
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
int t_visited=0;
int cycles=0;
int start=0, index;
while(t_visited < n)
{
for(int i=1;i<=n;i++)
{
if(visited[i]==0)
{
vindex[start]=i;
visited[i]=1;
t_visited++;
index=start;
break;
}
}
while(true)
{
index++;
vindex[index]=num[vindex[index-1]];
if(vindex[index]==vindex[start])
break;
visited[vindex[index]]=1;
t_visited++;
}
vindex[++index]=0;
start=index+1;
cycles++;
}
printf("%d\n",cycles,vindex[0]);
for(int i=0;i<(n+2*cycles);i++)
{
if(vindex[i]==0)
printf("\n");
else
printf("%d ",vindex[i]);
}
}
दूसरी मंजिल के उत्तर में छद्म कोड के लिए DFS c ++ संस्करण:
void findCircleUnit(int start, int v, bool* visited, vector<int>& path) {
if(visited[v]) {
if(v == start) {
for(auto c : path)
cout << c << " ";
cout << endl;
return;
}
else
return;
}
visited[v] = true;
path.push_back(v);
for(auto i : G[v])
findCircleUnit(start, i, visited, path);
visited[v] = false;
path.pop_back();
}