एक निर्देशित ग्राफ में सभी चक्रों का पता लगाना


198

मैं किसी दिए गए नोड से / से एक निर्देशित ग्राफ में सभी चक्रों को कैसे पा सकता हूं (इसे पुनरावृति करता हूं)?

उदाहरण के लिए, मुझे कुछ इस तरह चाहिए:

A->B->A
A->B->C->A

लेकिन नहीं: बी-> सी-> बी


1
होमवर्क मुझे लगता है? me.utexas.edu/~bard/IP/Handouts/cycles.pdf ऐसा नहीं है कि यह एक वैध प्रश्न नहीं है :)
ShuggyCoUk

5
ध्यान दें कि यह कम से कम एनपी हार्ड है। संभवतः PSPACE, मैं इसके बारे में सोचना होगा, लेकिन यह जटिलता सिद्धांत बी के लिए सुबह बहुत जल्दी है)
ब्रायन पोस्टो

2
यदि आपके इनपुट ग्राफ में v कोने और e किनारे हैं तो 2 ^ (e - v +1) -1 अलग-अलग चक्र हैं (हालांकि सभी सरल चक्र नहीं हो सकते हैं)। यह काफी है - आप स्पष्ट रूप से उन सभी को लिखना नहीं चाह सकते हैं। इसके अलावा, चूंकि आउटपुट का आकार घातीय है, एल्गोरिथ्म की जटिलता बहुपद नहीं हो सकती है। मुझे लगता है कि अभी भी इस सवाल का कोई जवाब नहीं है।
साइग्नसएक्स

1
मेरे लिए मेरा सबसे अच्छा विकल्प यह था: personal.kent.edu/~rmuhamma/Algorithms/MyAlgorithms/GraphAlgor/…
Melsi

जवाबों:


105

मुझे अपनी खोज में यह पृष्ठ मिला और चूंकि चक्र समान रूप से जुड़े घटकों के समान नहीं हैं, मैं खोज करता रहा और आखिरकार, मुझे एक कुशल एल्गोरिदम मिला, जो एक निर्देशित ग्राफ के सभी (प्राथमिक) चक्रों को सूचीबद्ध करता है। यह डोनाल्ड बी। जॉनसन का है और पेपर निम्न लिंक में पाया जा सकता है:

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

लेख के अनुसार, जॉनसन का एल्गोरिथ्म सबसे तेज़ है।


1
मुझे कागज से इसे लागू करने में इतनी परेशानी होती है, और अंततः इस एग्लोरिथम को अभी भी टारजन के कार्यान्वयन की आवश्यकता है। और जावा-कोड भी छुपा हुआ है। :(
ग्लेनो

7
@Gleno खैर, अगर आपका मतलब है कि आप टारजन का उपयोग ग्राफ को पूरा करने के बजाय बाकी के चक्रों को खोजने के लिए कर सकते हैं, तो आप गलत हैं। यहाँ , आप दृढ़ता से जुड़े घटकों और सभी चक्रों के बीच का अंतर देख सकते हैं (टारजन के alg द्वारा चक्र cd और gh को वापस नहीं किया जाएगा) (@ batbrat) आपकी उलझन का उत्तर भी यहाँ छिपा हुआ है: सभी संभव चक्रों को टारजन के द्वारा वापस नहीं किया गया है alg, इसलिए इसकी जटिलता घातीय से छोटी हो सकती है)। जावा-कोड बेहतर हो सकता है, लेकिन इसने मुझे कागज से लागू करने के प्रयास को बचाया।
एमिनसेन

4
यह उत्तर चयनित उत्तर की तुलना में बहुत बेहतर है। मैं काफी समय से यह जानने की कोशिश कर रहा था कि दृढ़ता से जुड़े घटकों से सभी सरल चक्र कैसे प्राप्त करें। यह गैर-तुच्छ है। जॉनसन के पेपर में एक महान एल्गोरिथ्म है, लेकिन इसके माध्यम से उतारा जाना थोड़ा मुश्किल है। मैंने जावा कार्यान्वयन को देखा और मतलब में अपना रोल किया। कोड gist.github.com/1260153 पर उपलब्ध है ।
कोडिअप्पो

5
@moteutsch: हो सकता है कि मैं कुछ याद कर रहा हूं, लेकिन जॉनसन पेपर (और अन्य स्रोतों) के अनुसार, एक चक्र प्राथमिक है यदि कोई शीर्ष (शुरुआत / खत्म होने के अलावा) एक से अधिक बार दिखाई देता है। उस परिभाषा के अनुसार, A->B->C->Aप्राथमिक भी नहीं है?
psmears

9
इसके लिए अजगर का उपयोग करने वाले किसी भी व्यक्ति के लिए ध्यान दें: जॉनसन एल्गोरिथ्म को simple_cycleनेटवर्कएक्स के रूप में लागू किया गया है।
जोएल

35

बैकट्रैकिंग के साथ पहली खोज को यहां काम करना चाहिए। पहले आपने एक नोड का दौरा किया या नहीं, इस पर नज़र रखने के लिए बूलियन मानों की एक सरणी रखें। यदि आप जाने के लिए नए नोड्स से बाहर निकलते हैं (एक नोड को हिट किए बिना), तो बस पीछे जाएं और एक अलग शाखा का प्रयास करें।

यदि आपके पास ग्राफ़ का प्रतिनिधित्व करने के लिए आसन्न सूची है तो 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)

2
धन्यवाद। मैं यहाँ कुछ अन्य लोगों के लिए इस दृष्टिकोण को पसंद करता हूँ क्योंकि यह समझने के लिए सरल (r) है और इसमें उचित समय जटिलता है, यद्यपि यह शायद इष्टतम है।
Redcalx

1
यह सब चक्र कैसे पता चलता है?
मस्तिष्क तूफान

3
if (node == start): - node and startपहली कॉल में क्या है
मस्तिष्क तूफान

2
@ user1988876 यह किसी दिए गए वर्टेक्स (जो होगा start) से जुड़े सभी चक्रों को ढूंढता है । यह उस शीर्ष पर शुरू होता है और एक डीएफएस करता है जब तक कि यह फिर से उस शीर्ष पर वापस नहीं जाता है, तब यह जानता है कि यह एक चक्र मिला। लेकिन यह वास्तव में चक्रों का उत्पादन नहीं करता है, बस उनमें से एक गिनती (लेकिन इसे संशोधित करने के लिए ऐसा करना चाहिए कि इसके बजाय बहुत मुश्किल नहीं होना चाहिए)।
बर्नहार्ड बार्कर

1
@ user1988876 ठीक है, यह सिर्फ प्रिंट करता है "पाया गया एक चक्र" चक्रों की संख्या के बराबर गुना है (इसे आसानी से एक गिनती द्वारा बदला जा सकता है)। हाँ, यह केवल चक्रों का पता लगाएगा start। आपको वास्तव में विज़िट किए गए ध्वज को साफ़ करने की आवश्यकता नहीं है क्योंकि प्रत्येक विज़िट किए गए ध्वज को साफ़ कर दिया जाएगा visited[node]=NO;। लेकिन ध्यान रखें कि यदि आपके पास एक चक्र है A->B->C->A, तो आप उस 3 बार का पता लगाएंगे , जैसा कि startउन 3 में से कोई भी हो सकता है। इसे रोकने के लिए एक विचार यह है कि एक और दौरा किया गया सरणी जहां प्रत्येक नोड जो startकिसी बिंदु पर नोड हो गया है, और उसके बाद आप इन पर दोबारा गौर नहीं करते हैं।
बर्नहार्ड बार्कर

23

सबसे पहले - आप वास्तव में सभी चक्रों को खोजने की कोशिश नहीं करना चाहते हैं क्योंकि यदि 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
Vishrant

19

इस समस्या को हल करने के लिए मुझे जो सबसे आसान विकल्प मिला, वह पायथन लिब नाम का प्रयोग था 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 सी ’]]

यहां छवि विवरण दर्ज करें


1
आप एक नेटवर्क ग्राफ के लिए एक डिक्शनरी भी बना सकते हैं:nx.DiGraph({'a': ['b'], 'b': ['c','d'], 'c': ['a'], 'd': ['e'], 'e':['a']})
ल्यूक मील्स

मैं एक प्रारंभिक शीर्ष कैसे निर्दिष्ट करूं?
नोसेंस

5

स्पष्टीकरण देना:

  1. मजबूत रूप से जुड़े हुए घटक सभी उपसमूहों को मिलेंगे जिनमें कम से कम एक चक्र होगा, ग्राफ में सभी संभव चक्र नहीं। उदाहरण के लिए यदि आप सभी दृढ़ता से जुड़े घटकों को लेते हैं और उनमें से प्रत्येक को एक नोड (यानी प्रति घटक एक नोड) में मिलाते हैं, तो आपको एक चक्र (डीएजी वास्तव में) के साथ एक पेड़ मिलेगा। प्रत्येक घटक (जो मूल रूप से कम से कम एक चक्र के साथ एक सबग्राफ है) में आंतरिक रूप से कई और संभावित चक्र शामिल हो सकते हैं, इसलिए एससीसी को सभी संभव चक्र नहीं मिलेंगे, इसमें सभी संभव समूह मिलेंगे जिनमें कम से कम एक चक्र हो, और यदि आप समूह उन्हें, तो ग्राफ में चक्र नहीं होगा।

  2. एक ग्राफ में सभी सरल चक्रों को खोजने के लिए , जैसा कि दूसरों ने उल्लेख किया है, जॉनसन का एल्गोरिथ्म एक उम्मीदवार है।


3

मुझे एक बार साक्षात्कार प्रश्न के रूप में दिया गया था, मुझे संदेह है कि यह आपके साथ हुआ है और आप मदद के लिए यहां आ रहे हैं। समस्या को तीन प्रश्नों में तोड़ें और यह आसान हो जाता है।

  1. आप अगला वैध मार्ग कैसे निर्धारित करते हैं
  2. यदि आप एक बिंदु का उपयोग किया गया है तो आप कैसे निर्धारित करेंगे
  3. आप फिर से उसी बिंदु को पार करने से कैसे बचते हैं

समस्या 1) पुनरावृत्ति मार्ग परिणामों का एक तरीका प्रदान करने के लिए पुनरावृत्ति पैटर्न का उपयोग करें। अगले मार्ग को प्राप्त करने के लिए तर्क रखने के लिए एक अच्छी जगह शायद आपके पुनरावृत्त का "मूव" है। एक वैध मार्ग खोजने के लिए, यह आपकी डेटा संरचना पर निर्भर करता है। मेरे लिए यह वैध मार्ग संभावनाओं से भरा एक वर्ग तालिका था इसलिए मुझे एक स्रोत दिए गए वैध स्थलों को प्राप्त करने के लिए एक क्वेरी का निर्माण करना था।

समस्या 2) प्रत्येक नोड को पुश करें जैसा कि आप उन्हें एक संग्रह में पाते हैं जैसा कि आप उन्हें प्राप्त करते हैं, इसका मतलब है कि आप देख सकते हैं कि क्या आप उस बिंदु पर बहुत आसानी से "दोहरीकरण" कर रहे हैं जिस संग्रह को आप फ्लाई पर बना रहे हैं।

समस्या 3) यदि किसी भी बिंदु पर आप देखते हैं कि आप दोहरीकरण कर रहे हैं, तो आप संग्रह और "बैक अप" से चीजों को पॉप कर सकते हैं। फिर उस बिंदु से फिर से "आगे बढ़ने" का प्रयास करें।

हैक: यदि आप Sql Server 2008 का उपयोग कर रहे हैं, तो कुछ नई "पदानुक्रम" चीजें हैं जिनका उपयोग आप जल्दी से इसे हल करने के लिए कर सकते हैं यदि आप किसी पेड़ में अपना डेटा बनाते हैं।


3

बैक किनारों वाले डीएफएस-आधारित वेरिएंट वास्तव में चक्र पाएंगे, लेकिन कई मामलों में यह न्यूनतम चक्र नहीं होगा । सामान्य तौर पर 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

2

अप्रत्यक्ष ग्राफ़ के मामले में , हाल ही में प्रकाशित एक पेपर ( अप्रत्यक्ष ग्राफ़ में साइकिल और सेंट-पथ की इष्टतम सूची ) एक asymptotically इष्टतम समाधान प्रदान करता है। आप इसे पढ़ सकते हैं यहाँ http://arxiv.org/abs/1205.2766 या यहाँ http://dl.acm.org/citation.cfm?id=2627951 मैं इसे आपके प्रश्न का उत्तर नहीं है पता है, लेकिन के शीर्षक के बाद से आपका प्रश्न दिशा का उल्लेख नहीं करता है, यह अभी भी Google खोज के लिए उपयोगी हो सकता है


1

नोड एक्स पर शुरू करें और सभी बच्चे नोड्स के लिए जांचें (यदि अप्रत्यक्ष हैं तो माता-पिता और बच्चे के नोड बराबर हैं)। उन बच्चों के नोड्स को X के बच्चे होने के रूप में चिह्नित करें। ऐसे किसी भी बच्चे के नोड ए से, यह ए, एक्स 'के बच्चे होने का निशान है, जहां एक्स' को 2 कदम दूर होने के रूप में चिह्नित किया गया है।)। यदि आप बाद में X को हिट करते हैं और इसे X '' का बच्चा होने के रूप में चिह्नित करते हैं, तो इसका मतलब है कि X 3 नोड चक्र में है। माता-पिता के लिए इसे वापस लेना आसान है (जैसा कि, एल्गोरिथ्म के पास इसके लिए कोई समर्थन नहीं है, ताकि आपको पता चले कि जो भी माता-पिता के पास X है)।

नोट: यदि ग्राफ़ अप्रत्यक्ष है या किसी भी द्विदिश किनारों है, तो यह एल्गोरिथ्म अधिक जटिल हो जाता है, यह मानते हुए कि आप एक चक्र के लिए दो बार एक ही किनारे को पार नहीं करना चाहते हैं।


1

यदि आप चाहते हैं कि एक ग्राफ में सभी प्राथमिक सर्किटों को ढूंढना है, तो आप 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

?>

मैंने चुनाव आयोग को सूचित किया और दस्तावेज दिया लेकिन दुर्भाग्य से यह दस्तावेज ग्रीक में है।


1

DAG में सभी चक्रों को खोजने में दो चरण (एल्गोरिदम) शामिल हैं।

दृढ़ता से जुड़े घटकों के सेट को खोजने के लिए पहला कदम टारजन के एल्गोरिथ्म का उपयोग करना है।

  1. किसी भी मनमानी से शुरू करें।
  2. उस शीर्ष से डीएफएस। प्रत्येक नोड x के लिए, दो नंबर रखें, dfs_index [x] और dfs_lowval [x]। dfs_index [x] स्टोर जब उस नोड का दौरा किया जाता है, जबकि dfs_lowval [x] = min (dfs_low [k]) जहां k x के सभी बच्चे होते हैं जो dfs- फैले हुए पेड़ में x के सीधे माता-पिता नहीं होते हैं।
  3. समान dfs_lowval [x] के साथ सभी नोड समान रूप से जुड़े घटक में हैं।

दूसरा चरण जुड़ा घटकों के भीतर चक्र (पथ) को खोजने के लिए है। मेरा सुझाव Hierholzer के एल्गोरिथ्म के संशोधित संस्करण का उपयोग करना है।

विचार यह है:

  1. किसी भी शुरुआती वर्सेट वी को चुनें, और जब तक आप वी पर वापस नहीं आते हैं, तब तक उस शीर्ष से किनारों का एक निशान का पालन करें। वी के अलावा किसी भी शीर्ष पर अटकना संभव नहीं है, क्योंकि सभी कोने की समान डिग्री यह सुनिश्चित करती है, जब निशान दूसरे में प्रवेश करता है वर्टेक्स डब्ल्यू में एक अप्रयुक्त किनारे होना चाहिए जो डब्ल्यू को छोड़ देगा। इस तरह से बनाया गया दौरा एक बंद दौरा है, लेकिन प्रारंभिक ग्राफ के सभी कोने और किनारों को कवर नहीं कर सकता है।
  2. जब तक एक वर्सेटाइल वी मौजूद होता है जो वर्तमान टूर से संबंधित होता है, लेकिन उसके आस-पास के किनारे टूर का हिस्सा नहीं होते हैं, वी से एक और निशान शुरू करते हैं, अप्रयुक्त किनारों का अनुसरण करते हुए जब तक आप वी पर वापस नहीं आते हैं, और इस तरह से बने टूर में शामिल हो जाते हैं। पिछला दौरा।

एक परीक्षण मामले के साथ जावा कार्यान्वयन का लिंक यहां दिया गया है:

http://stones333.blogspot.com/2013/12/find-cycles-in-directed-graph-dag.html


16
DAG (डायरेक्टेड एसाइक्लिक ग्राफ) में एक चक्र कैसे मौजूद हो सकता है?
sky_coder123

यह सभी चक्र नहीं पाते हैं।
विश्व रत्न

0

प्रश्न निर्देशित ग्राफ़ में चक्रों को हटाने के बारे में था, लेकिन यह दस्तावेज़ अप्रत्यक्ष लोगों पर है।
izilotti

0

मैं निम्नलिखित एल्गोरिथ्म पर ठोकर खाई जो जॉनसन के एल्गोरिथ्म की तुलना में अधिक कुशल प्रतीत होता है (कम से कम बड़े ग्राफ़ के लिए)। मैं हालांकि टार्जन के एल्गोरिथ्म की तुलना में इसके प्रदर्शन के बारे में निश्चित नहीं हूं।
इसके अतिरिक्त, मैंने केवल इसे अभी तक त्रिकोणों के लिए जांचा। यदि रुचि है, तो कृपया नॉरिशिज चिबा और ताकाओ निशिज़की ( http://dx.doi.org/10.1137/0214017 ) द्वारा "आर्बरिटी और सबग्राफ लिस्टिंग एल्गोरिदम" देखें।


0

जावास्क्रिप्ट सेट लिंक सूचियों का उपयोग करते हुए जावास्क्रिप्ट समाधान। तेजी से चलने वाले समय के लिए सेट जंगलों को अपग्रेड किया जा सकता है।

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;
}

0

शुरू नोड एस से डीएफएस, ट्रैवर्सल के दौरान डीएफएस पथ का ट्रैक रखें, और पथ को रिकॉर्ड करें यदि आप नोड वी से एस के रास्ते में बढ़त पाते हैं। (v, s) डीएफएस पेड़ में एक बैक-एज है और इस तरह एक चक्र होता है जिसमें s होता है।


अच्छा है, लेकिन यह वह नहीं है जो ओपी की तलाश में है: सभी चक्र खोजें, न्यूनतम संभावना।
सीन एल

0

क्रमचय चक्र के बारे में आपके प्रश्न के बारे में , यहाँ और अधिक पढ़ें: 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]);
    }
}

0

दूसरी मंजिल के उत्तर में छद्म कोड के लिए 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();
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.