चूँकि इस उत्तर में दिया गया गैर-पुनरावर्ती डीएफएस कार्यान्वयन टूटा हुआ प्रतीत होता है, मुझे वह प्रदान करें जो वास्तव में काम करता है।
मैंने इसे पायथन में लिखा है, क्योंकि मैं इसे कार्यान्वयन विवरण (और क्योंकि इसमें जनरेटरyield
को लागू करने के लिए आसान कीवर्ड है ) द्वारा इसे बहुत पठनीय और अस्पष्ट पाया गया है , लेकिन इसे अन्य भाषाओं में पोर्ट करना काफी आसान होना चाहिए।
# a generator function to find all simple paths between two nodes in a
# graph, represented as a dictionary that maps nodes to their neighbors
def find_simple_paths(graph, start, end):
visited = set()
visited.add(start)
nodestack = list()
indexstack = list()
current = start
i = 0
while True:
# get a list of the neighbors of the current node
neighbors = graph[current]
# find the next unvisited neighbor of this node, if any
while i < len(neighbors) and neighbors[i] in visited: i += 1
if i >= len(neighbors):
# we've reached the last neighbor of this node, backtrack
visited.remove(current)
if len(nodestack) < 1: break # can't backtrack, stop!
current = nodestack.pop()
i = indexstack.pop()
elif neighbors[i] == end:
# yay, we found the target node! let the caller process the path
yield nodestack + [current, end]
i += 1
else:
# push current node and index onto stacks, switch to neighbor
nodestack.append(current)
indexstack.append(i+1)
visited.add(neighbors[i])
current = neighbors[i]
i = 0
यह कोड दो समानांतर स्टैक को बनाए रखता है: वर्तमान पथ में पहले नोड्स वाले एक, और नोड स्टैक में प्रत्येक नोड के लिए वर्तमान पड़ोसी सूचकांक वाले एक (ताकि हम एक नोड के पड़ोसियों के माध्यम से पुनरावृत्ति शुरू कर सकें जब हम इसे वापस पॉप करते हैं। ढेर)। मैं समान रूप से (नोड, इंडेक्स) जोड़े के एक ही ढेर का उपयोग कर सकता था, लेकिन मुझे लगा कि दो-स्टैक विधि अधिक पठनीय होगी, और शायद अन्य भाषाओं के उपयोगकर्ताओं के लिए लागू करना आसान है।
यह कोड एक अलग visited
सेट का भी उपयोग करता है , जिसमें हमेशा वर्तमान नोड और स्टैक पर कोई नोड होता है, मुझे कुशलतापूर्वक जांचने के लिए कि क्या नोड पहले से ही मौजूदा पथ का हिस्सा है। यदि आपकी भाषा में "ऑर्डर सेट" डेटा संरचना है जो दोनों कुशल स्टैक-जैसे पुश / पॉप संचालन और कुशल सदस्यता क्वेरी प्रदान करती है, तो आप नोड स्टैक के लिए उपयोग कर सकते हैं और अलग visited
सेट से छुटकारा पा सकते हैं ।
वैकल्पिक रूप से, यदि आप अपने नोड्स के लिए एक कस्टम म्यूटेबल क्लास / संरचना का उपयोग कर रहे हैं, तो आप यह इंगित करने के लिए कि क्या यह वर्तमान खोज पथ के हिस्से के रूप में देखा गया है, प्रत्येक नोड में एक बूलियन ध्वज को संग्रहीत कर सकता है। बेशक, यह विधि आपको समानांतर में एक ही ग्राफ पर दो खोजों को चलाने की अनुमति नहीं देगी, आपको ऐसा करने के लिए किसी कारण से चाहिए।
यहां कुछ परीक्षण कोड दर्शाए गए हैं कि ऊपर दिए गए फ़ंक्शन कैसे काम करते हैं:
# test graph:
# ,---B---.
# A | D
# `---C---'
graph = {
"A": ("B", "C"),
"B": ("A", "C", "D"),
"C": ("A", "B", "D"),
"D": ("B", "C"),
}
# find paths from A to D
for path in find_simple_paths(graph, "A", "D"): print " -> ".join(path)
दिए गए उदाहरण ग्राफ पर इस कोड को चलाने से निम्न आउटपुट का उत्पादन होता है:
ए -> बी -> सी -> डी
ए -> बी -> डी
ए -> सी -> बी -> डी
ए -> सी -> डी
ध्यान दें, जबकि यह उदाहरण ग्राफ़ अप्रत्यक्ष है (अर्थात इसके सभी किनारे दोनों तरह से चलते हैं), एल्गोरिथ्म भी मनमाने ढंग से निर्देशित ग्राफ़ के लिए काम करता है। उदाहरण के लिए, C -> B
किनारे को हटाने ( B
पड़ोसी की सूची से हटाकर C
) तीसरे पथ ( A -> C -> B -> D
) को छोड़कर एक ही आउटपुट देता है, जो अब संभव नहीं है।
Ps।ऐसे ग्राफ़ बनाना आसान है जिनके लिए सरल खोज एल्गोरिदम इस तरह (और इस धागे में दिए गए अन्य) बहुत खराब प्रदर्शन करते हैं।
उदाहरण के लिए, एक अप्रत्यक्ष ग्राफ पर ए से बी तक के सभी रास्तों को खोजने के कार्य पर विचार करें, जहां शुरुआती नोड में दो पड़ोसी हैं: लक्ष्य नोड बी (जिसमें ए के अलावा कोई अन्य पड़ोसी नहीं है) और एक नोड सी जो एक गुट का हिस्सा है के एन +1 नोड्स, इस तरह:
graph = {
"A": ("B", "C"),
"B": ("A"),
"C": ("A", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"),
"D": ("C", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"),
"E": ("C", "D", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"),
"F": ("C", "D", "E", "G", "H", "I", "J", "K", "L", "M", "N", "O"),
"G": ("C", "D", "E", "F", "H", "I", "J", "K", "L", "M", "N", "O"),
"H": ("C", "D", "E", "F", "G", "I", "J", "K", "L", "M", "N", "O"),
"I": ("C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "O"),
"J": ("C", "D", "E", "F", "G", "H", "I", "K", "L", "M", "N", "O"),
"K": ("C", "D", "E", "F", "G", "H", "I", "J", "L", "M", "N", "O"),
"L": ("C", "D", "E", "F", "G", "H", "I", "J", "K", "M", "N", "O"),
"M": ("C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "N", "O"),
"N": ("C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "O"),
"O": ("C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N"),
}
यह देखना आसान है कि ए और बी के बीच का एकमात्र मार्ग सीधा है, लेकिन नोड ए से शुरू हुआ एक भोले डीएफएस ओ ( एन !) समय बर्बाद कर देगा , जबकि यह स्पष्ट रूप से (एक मानव के लिए) क्लिक के भीतर बेकार के रास्ते तलाश रहा है, उन रास्तों में से कोई भी संभवतः बी को जन्म दे सकता है।
एक भी निर्माण कर सकते हैं DAGs शुरू करने नोड एक कनेक्ट लक्ष्य नोड बी और दो अन्य नोड्स सेल्सियस के लिए होने से समान गुणों, जैसे के साथ 1 और सी 2 डी, जो दोनों के नोड्स से कनेक्ट 1 और डी 2 , जो दोनों के ई से कनेक्ट 1 और ई 2 , और इसी तरह। इस तरह से व्यवस्थित किए गए नोड्स की एन परतों के लिए , ए से बी तक के सभी रास्तों के लिए एक भोली खोज ओ को नष्ट कर देगी (2 एन) ) समय जो देने से पहले सभी संभावित मृत सिरों की जांच करेगा।
बेशक, लक्ष्य नोड B के एक छोर को Clique (C के अलावा अन्य) में से एक या DAG की अंतिम परत से जोड़कर, A से B तक के संभावित रास्तों की एक बड़ी संख्या का निर्माण होगा , और एक विशुद्ध रूप से स्थानीय खोज एल्गोरिथ्म वास्तव में पहले से नहीं बता सकता है कि यह इस तरह का एक किनारा मिलेगा या नहीं। इस प्रकार, एक अर्थ में, ऐसी भोली खोजों की खराब आउटपुट संवेदनशीलता उनके ग्राफ की वैश्विक संरचना के बारे में जागरूकता की कमी के कारण है।
जबकि विभिन्न प्रीप्रोसेसिंग विधियाँ हैं (जैसे कि पुनरावृत्त पत्ती के नोड्स को समाप्त करना, एकल-नोड वर्टेक्स विभाजकों की खोज करना, आदि) जो इन "घातीय-समय मृत सिरों" में से कुछ से बचने के लिए इस्तेमाल किया जा सकता है, मुझे किसी भी सामान्य के बारे में पता नहीं है। प्रीप्रोसेसिंग ट्रिक जो उन्हें सभी मामलों में खत्म कर सकती है । एक सामान्य समाधान यह होगा कि आप खोज के हर चरण की जांच करें कि क्या लक्ष्य नोड अभी भी उपलब्ध है (उप-खोज का उपयोग करके), और यदि यह नहीं है तो जल्दी से पीछे हट जाएं - लेकिन अफसोस, यह खोज को काफी धीमा कर देगा (सबसे खराब रूप से) आनुपातिक रूप से ग्राफ़ के आकार के लिए) कई ग्राफ़ के लिए, जिसमें इस तरह के पैथोलॉजिकल डेड एंड्स नहीं होते हैं।