चौड़ाई-प्रथम खोज में पथ कैसे खोजें?


104

आप चौड़ाई-प्रथम खोज का मार्ग कैसे खोजते हैं, जैसे कि निम्नलिखित उदाहरण में:

यदि कुंजी खोज रहे हैं 11, तो 1 से 11 को जोड़ने वाली सबसे छोटी सूची लौटाएं ।

[1, 4, 7, 11]

6
यह वास्तव में एक पुराना असाइनमेंट था जो मैं केविन बेकन लॉ के आधार पर महीनों पहले एक दोस्त की मदद कर रहा था। मेरा अंतिम समाधान बहुत मैला था, मैंने मूल रूप से "रिवाइंड" और बैकट्रैक के लिए एक और चौड़ाई-पहली खोज की। मैं एक बेहतर समाधान खोजने के लिए नहीं था।
क्रिस्टोफर मार्किटा

21
अति उत्कृष्ट। मैं एक इंजीनियर में एक सराहनीय गुण होने के लिए एक बेहतर जवाब खोजने की कोशिश में एक पुरानी समस्या पर फिर से गौर करने पर विचार करता हूं। मैं आपकी पढ़ाई और करियर में आपके अच्छे होने की कामना करता हूं।
पीटर रोवेल

1
प्रशंसा के लिए धन्यवाद, मुझे विश्वास है कि अगर मैं इसे अभी नहीं सीखता हूं, तो मुझे फिर से उसी समस्या का सामना करना पड़ेगा।
क्रिस्टोफर मार्किटा

जवाबों:


194

आपको पहले http://en.wikipedia.org/wiki/Breadth-first_search को देखना चाहिए ।


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

# graph is in adjacent list representation
graph = {
        '1': ['2', '3', '4'],
        '2': ['5', '6'],
        '5': ['9', '10'],
        '4': ['7', '8'],
        '7': ['11', '12']
        }

def bfs(graph, start, end):
    # maintain a queue of paths
    queue = []
    # push the first path into the queue
    queue.append([start])
    while queue:
        # get the first path from the queue
        path = queue.pop(0)
        # get the last node from the path
        node = path[-1]
        # path found
        if node == end:
            return path
        # enumerate all adjacent nodes, construct a new path and push it into the queue
        for adjacent in graph.get(node, []):
            new_path = list(path)
            new_path.append(adjacent)
            queue.append(new_path)

print bfs(graph, '1', '11')

एक अन्य दृष्टिकोण प्रत्येक नोड से उसके माता-पिता तक मानचित्रण बनाए रखना होगा, और जब आसन्न नोड का निरीक्षण किया जाएगा, तो उसके माता-पिता को रिकॉर्ड करें। जब खोज पूरी हो जाती है, तो मूल माता-पिता के अनुसार बैकट्रेस करें।

graph = {
        '1': ['2', '3', '4'],
        '2': ['5', '6'],
        '5': ['9', '10'],
        '4': ['7', '8'],
        '7': ['11', '12']
        }

def backtrace(parent, start, end):
    path = [end]
    while path[-1] != start:
        path.append(parent[path[-1]])
    path.reverse()
    return path


def bfs(graph, start, end):
    parent = {}
    queue = []
    queue.append(start)
    while queue:
        node = queue.pop(0)
        if node == end:
            return backtrace(parent, start, end)
        for adjacent in graph.get(node, []):
            if node not in queue :
                parent[adjacent] = node # <<<<< record its parent 
                queue.append(adjacent)

print bfs(graph, '1', '11')

उपरोक्त कोड इस धारणा पर आधारित हैं कि कोई चक्र नहीं है।


2
यह उत्कृष्ट है! मेरी विचार प्रक्रिया मुझे किसी प्रकार की तालिका या मैट्रिक्स बनाने में विश्वास करने के लिए प्रेरित करती है, मुझे अभी तक ग्राफ़ के बारे में सीखना है। धन्यवाद।
क्रिस्टोफर मार्किटा

मैंने एक बैक ट्रेसिंग दृष्टिकोण का उपयोग करने की भी कोशिश की, हालांकि यह बहुत साफ लगता है। क्या यह एक ग्राफ बनाना संभव होगा यदि आप केवल शुरुआत और अंत जानते हैं, लेकिन बीच में कोई भी नोड नहीं है? या रेखांकन के अलावा एक और दृष्टिकोण?
क्रिस्टोफर मार्किटा

@ChristopherM मैं आपके प्रश्न को समझने में असफल रहा :(
qiao

1
क्या पहले एल्गोरिथ्म को अनुकूलित करना संभव है ताकि यह 1 से 11 तक सभी रास्तों को वापस कर देगा (यह मानते हुए कि एक से अधिक है)?
मारिया इनेस पर्निसारी

1
यह एक सूची के बजाय collection.deque का उपयोग करने के लिए अनुशंसित है। list.pop (0) की जटिलता O (n) है, जबकि deque.popleft () O (1) है
Omar_0x80

23

मुझे qiao का पहला उत्तर बहुत पसंद आया! यहां केवल एक चीज गायब है, जैसा कि दौरा किया गया है।

हमें इसे करने की आवश्यकता क्यों है?
चलो कल्पना करते हैं कि नोड 11 से जुड़ा एक और नोड नंबर 13 है। अब हमारा लक्ष्य नोड 13 को ढूंढना है।
थोड़ी देर चलने के बाद कतार इस तरह दिखाई देगी:

[[1, 2, 6], [1, 3, 10], [1, 4, 7], [1, 4, 8], [1, 2, 5, 9], [1, 2, 5, 10]]

ध्यान दें कि अंत में नोड नंबर 10 के साथ दो मार्ग हैं।
जिसका मतलब है कि नोड नंबर 10 से रास्तों को दो बार जांचा जाएगा। इस मामले में यह इतना बुरा नहीं लगता है क्योंकि नोड नंबर 10 के कोई बच्चे नहीं हैं .. लेकिन यह वास्तव में बुरा हो सकता है (यहां तक ​​कि हम बिना किसी कारण दो बार उस नोड की जांच करेंगे ..)
नोड नंबर 13 में नहीं है उन रास्तों ताकि अंत में नोड नंबर 10 के साथ दूसरे मार्ग पर पहुंचने से पहले कार्यक्रम वापस नहीं आएगा..और हम इसे फिर से जाँचेंगे ..

हम सभी को याद कर रहे हैं, एक बार दौरा किए गए नोड्स को चिह्नित करने और उन्हें फिर से जांचने के लिए नहीं है ..
यह संशोधन के बाद qiao का कोड है:

graph = {
    1: [2, 3, 4],
    2: [5, 6],
    3: [10],
    4: [7, 8],
    5: [9, 10],
    7: [11, 12],
    11: [13]
}


def bfs(graph_to_search, start, end):
    queue = [[start]]
    visited = set()

    while queue:
        # Gets the first path in the queue
        path = queue.pop(0)

        # Gets the last node in the path
        vertex = path[-1]

        # Checks if we got to the end
        if vertex == end:
            return path
        # We check if the current node is already in the visited nodes set in order not to recheck it
        elif vertex not in visited:
            # enumerate all adjacent nodes, construct a new path and push it into the queue
            for current_neighbour in graph_to_search.get(vertex, []):
                new_path = list(path)
                new_path.append(current_neighbour)
                queue.append(new_path)

            # Mark the vertex as visited
            visited.add(vertex)


print bfs(graph, 1, 13)

कार्यक्रम का आउटपुट होगा:

[1, 4, 7, 11, 13]

बेवजह के बिना रीटेक ।।


6
यह list.pop (0) के रूप में उपयोग के collections.dequeलिए उपयोगी हो सकता है स्मृति आंदोलनों। इसके अलावा, पश्चात की स्थिति के लिए, यदि आप डीएफएस करना चाहते हैं तो बस सेट करें जिस स्थिति में चर वास्तव में ए की तरह कार्य करता है । queueO(n)path = queue.pop()queuestack
सुधी

11

बहुत आसान कोड। हर बार जब आप एक नोड की खोज करते हैं तो आप पथ को जोड़ते रहते हैं।

graph = {
         'A': set(['B', 'C']),
         'B': set(['A', 'D', 'E']),
         'C': set(['A', 'F']),
         'D': set(['B']),
         'E': set(['B', 'F']),
         'F': set(['C', 'E'])
         }
def retunShortestPath(graph, start, end):

    queue = [(start,[start])]
    visited = set()

    while queue:
        vertex, path = queue.pop(0)
        visited.add(vertex)
        for node in graph[vertex]:
            if node == end:
                return path + [end]
            else:
                if node not in visited:
                    visited.add(node)
                    queue.append((node, path + [node]))

2
मुझे अन्य उत्तरों की तुलना में आपका कोड बहुत पठनीय लगता है। आपका बहुत बहुत धन्यवाद!
मित्को रुसेव

8

मैंने सोचा कि मैं इस कोड को मज़े के लिए आज़माऊंगा:

graph = {
        '1': ['2', '3', '4'],
        '2': ['5', '6'],
        '5': ['9', '10'],
        '4': ['7', '8'],
        '7': ['11', '12']
        }

def bfs(graph, forefront, end):
    # assumes no cycles

    next_forefront = [(node, path + ',' + node) for i, path in forefront if i in graph for node in graph[i]]

    for node,path in next_forefront:
        if node==end:
            return path
    else:
        return bfs(graph,next_forefront,end)

print bfs(graph,[('1','1')],'11')

# >>>
# 1, 4, 7, 11

यदि आप चक्र चाहते हैं तो आप इसे जोड़ सकते हैं:

for i, j in for_front: # allow cycles, add this code
    if i in graph:
        del graph[i]

के बाद आप ने next_for_front बनाया है। प्रश्न पर एक अनुसरण करें, क्या होगा यदि ग्राफ़ में लूप शामिल हैं? उदाहरण के लिए, यदि नोड 1 में एक किनारे खुद को वापस जोड़ता था? क्या होगा यदि ग्राफ़ में दो किनारों के बीच कई किनारों को जा रहा है?
रॉबर्ट राजा

1

मुझे @Qiao का पहला उत्तर और @ या का जोड़ पसंद है। थोड़े कम प्रसंस्करण के लिए मैं या के उत्तर में जोड़ना चाहूंगा।

में @ या का दौरा रखा नोड का जवाब बहुत अच्छा है। हम प्रोग्राम को जल्द से जल्द बाहर निकलने की अनुमति भी दे सकते हैं जो वर्तमान में है। लूप के लिए कुछ बिंदु पर current_neighbourइच्छाशक्ति होनी चाहिए end, और ऐसा होने पर सबसे छोटा रास्ता मिल जाता है और प्रोग्राम वापस आ सकता है।

मैं विधि का पालन के रूप में संशोधित करेगा, लूप के लिए करीब ध्यान दें

graph = {
1: [2, 3, 4],
2: [5, 6],
3: [10],
4: [7, 8],
5: [9, 10],
7: [11, 12],
11: [13]
}


    def bfs(graph_to_search, start, end):
        queue = [[start]]
        visited = set()

    while queue:
        # Gets the first path in the queue
        path = queue.pop(0)

        # Gets the last node in the path
        vertex = path[-1]

        # Checks if we got to the end
        if vertex == end:
            return path
        # We check if the current node is already in the visited nodes set in order not to recheck it
        elif vertex not in visited:
            # enumerate all adjacent nodes, construct a new path and push it into the queue
            for current_neighbour in graph_to_search.get(vertex, []):
                new_path = list(path)
                new_path.append(current_neighbour)
                queue.append(new_path)

                #No need to visit other neighbour. Return at once
                if current_neighbour == end
                    return new_path;

            # Mark the vertex as visited
            visited.add(vertex)


print bfs(graph, 1, 13)

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.