पायथन में रेखांकन (डेटा संरचना) का प्रतिनिधित्व करना


105

पायथन में एक ग्राफ को बड़े करीने से कैसे दर्शाया जा सकता है ? (स्क्रैच से शुरू करना अर्थात कोई लाइब्रेरी नहीं!) क्या डेटा संरचना (जैसे डिकट्स / ट्यूपल्स / तानाशाही (ट्यूपल्स)) तेज होगी, बल्कि मेमोरी कुशल भी होगी? एक को इस पर विभिन्न ग्राफ संचालन करने में सक्षम होना चाहिए । जैसा कि बताया गया है, विभिन्न ग्राफ अभ्यावेदन मदद कर सकते हैं। पायथन में उन्हें लागू करने के बारे में कैसे जाना जाता है? पुस्तकालयों के लिए, इस प्रश्न के काफी अच्छे उत्तर हैं।






1
वहाँ बहुत सारे पुस्तकालय पहले से ही मौजूद हैं: ग्राफ़-tool.skewed.de/performance , code.google.com/p/python-graph , networkx.github.io
Kassym Dorsel

1
विकिपीडिया लेख पर ग्राफ़ को लागू करने के लिए, जो स्मृति और गति दोनों में सामान्य कार्यान्वयन और उनकी दक्षता को सूचीबद्ध करता है: en.wikipedia.org/wiki/…
Kassym Dorsel

आप GitHub.com/thePastor/pangaia की कोशिश कर सकते हैं। मानक पुस्तकालय के डिफॉल्ट का उपयोग करने के लिए इसे थोड़ा पुनर्लेखन की आवश्यकता है (जो कोड लिखे जाने पर बाहर नहीं था)। यह अन्य कार्यान्वयन की तुलना में अधिक सुरुचिपूर्ण बनाने के लिए एक पुनरावर्ती डेटा संरचना का उपयोग करता है।
theDoctor

1
के लिए निर्देशित रेखांकन, इस python.org से निबंध एक पता चलता है dictकी listरों। मूल रूप से कुछ पसंद है {<parent>: [<child>, ...], ...}
djvg

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

जवाबों:


140

हालांकि यह एक बहुत पुराना सवाल है, मैंने सोचा कि मैं किसी को भी इस पार ठोकर खाने के लिए व्यावहारिक जवाब दूंगा।

मान लीजिए कि आप अपने कनेक्शन के लिए अपना इनपुट डेटा प्राप्त करते हैं जैसे ट्यूपल्स की सूची:

[('A', 'B'), ('B', 'C'), ('B', 'D'), ('C', 'D'), ('E', 'F'), ('F', 'C')]

पायथन में रेखांकन के लिए मैंने जो डेटा संरचना सबसे उपयोगी और कुशल पाई है, वह सेट का एक ताना-बाना है । यह हमारी Graphकक्षा के लिए अंतर्निहित संरचना होगी । आपको यह भी जानना होगा कि क्या ये कनेक्शन आर्क्स हैं (निर्देशित, एक तरह से कनेक्ट करें) या किनारों (अप्रत्यक्ष, दोनों तरीकों से कनेक्ट करें)। हम उस directedपैरामीटर को Graph.__init__विधि में जोड़कर संभाल लेंगे । हम कुछ अन्य सहायक विधियाँ भी जोड़ेंगे।

import pprint
from collections import defaultdict


class Graph(object):
    """ Graph data structure, undirected by default. """

    def __init__(self, connections, directed=False):
        self._graph = defaultdict(set)
        self._directed = directed
        self.add_connections(connections)

    def add_connections(self, connections):
        """ Add connections (list of tuple pairs) to graph """

        for node1, node2 in connections:
            self.add(node1, node2)

    def add(self, node1, node2):
        """ Add connection between node1 and node2 """

        self._graph[node1].add(node2)
        if not self._directed:
            self._graph[node2].add(node1)

    def remove(self, node):
        """ Remove all references to node """

        for n, cxns in self._graph.items():  # python3: items(); python2: iteritems()
            try:
                cxns.remove(node)
            except KeyError:
                pass
        try:
            del self._graph[node]
        except KeyError:
            pass

    def is_connected(self, node1, node2):
        """ Is node1 directly connected to node2 """

        return node1 in self._graph and node2 in self._graph[node1]

    def find_path(self, node1, node2, path=[]):
        """ Find any path between node1 and node2 (may not be shortest) """

        path = path + [node1]
        if node1 == node2:
            return path
        if node1 not in self._graph:
            return None
        for node in self._graph[node1]:
            if node not in path:
                new_path = self.find_path(node, node2, path)
                if new_path:
                    return new_path
        return None

    def __str__(self):
        return '{}({})'.format(self.__class__.__name__, dict(self._graph))

मैं इसे "पाठक के लिए व्यायाम" के रूप में एक find_shortest_pathऔर अन्य तरीकों को बनाने के लिए छोड़ दूँगा ।

आइए देखते हैं इस एक्शन में हालांकि ...

>>> connections = [('A', 'B'), ('B', 'C'), ('B', 'D'),
                   ('C', 'D'), ('E', 'F'), ('F', 'C')]
>>> g = Graph(connections, directed=True)
>>> pretty_print = pprint.PrettyPrinter()
>>> pretty_print.pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'C'},
 'C': {'D'},
 'E': {'F'},
 'F': {'C'}}

>>> g = Graph(connections)  # undirected
>>> pretty_print = pprint.PrettyPrinter()
>>> pretty_print.pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'A', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'B'},
 'E': {'F'},
 'F': {'E', 'C'}}

>>> g.add('E', 'D')
>>> pretty_print.pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'A', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'}}

>>> g.remove('A')
>>> pretty_print.pprint(g._graph)
{'B': {'D', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'}}

>>> g.add('G', 'B')
>>> pretty_print.pprint(g._graph)
{'B': {'D', 'G', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'},
 'G': {'B'}}

>>> g.find_path('G', 'E')
['G', 'B', 'D', 'C', 'F', 'E']

6
भले ही यह सवाल बहुत पुराना है, लेकिन मुझे लगता है कि यह ठीक उसी तरह का उत्तर है जिसकी मुझे उस समय उम्मीद थी। उदाहरण वास्तव में यह समझाने में मदद करता है कि एक ही समय में कार्यान्वयन के बारे में कैसे जा सकता है यह वास्तव में सरल रखते हुए। कोई भी अलग-अलग ओपन सोर्स लाइब्रेरी से कार्यान्वयन पा सकता है, लेकिन स्पष्टीकरण सममूल्य पर नहीं होगा। धन्यवाद!
shad0w_wa1k3r

2
किनारों पर वजन जोड़ने के लिए किस तरह के संशोधन की आवश्यकता है?
pshirishreddy

3
@pshirishreddy दिलचस्प सवाल! मैंने उस बारे में नहीं सोचा था, लेकिन मेरी वृत्ति heapqसेट के बजाय टुपल्स की सूचियों को हटाने के लिए उपयोग करने के लिए होगी । उदाहरण के लिए ग्राफ एक ढेर जैसा होगा: _graph = {'A': heapify([(0.3, 'D'), (0.5, 'B'), (0.75, 'A'), (0.9, 'C')])}(नोट: आप वास्तव में heapifyइस तरह का उपयोग नहीं करेंगे , परिवाद के लिए मदद पढ़ें), फिर आप heapqभारित किनारों को सम्मिलित करने और प्राप्त करने के लिए फ़ंक्शन का उपयोग कर सकते हैं ।
mVChr

@ एमवीसीएचआर का मतलब होता है logसमय की पहुंच। लेकिन उस शब्दकोश का विस्तार कैसे करें जो आपने नोड और वजन दोनों के लिए उपयोग किया था?
orezvani

अच्छा लगा! फ़ंक्शन को पुनरावर्ती कहा जाता है। यह एक डीएफएस लगता है क्योंकि यह नोड्स का विस्तार करता रहता है। सबसे छोटे मार्ग के लिए हम पथों की लंबाई की तुलना कर सकते हैं और अंत में केवल सबसे छोटा रास्ता लौटा सकते हैं।
ज्वलंत भट्ट

36

NetworkX एक भयानक पायथन ग्राफ लाइब्रेरी है। आपको कुछ ऐसी चीज़ खोजने के लिए मुश्किल से दबाया जाएगा जिसकी आपको आवश्यकता है जो पहले से नहीं है।

और यह खुला स्रोत है ताकि आप देख सकें कि उन्होंने अपने एल्गोरिदम को कैसे लागू किया। आप अतिरिक्त एल्गोरिदम भी जोड़ सकते हैं।

https://github.com/networkx/networkx/tree/master/networkx/algorithms


7
यही कारण है कि NetworkX एक शानदार संसाधन है। यह खुला स्रोत है ताकि आप देख सकें कि उन्होंने अपने एल्गोरिदम को कैसे लागू किया। आप अतिरिक्त एल्गोरिदम भी जोड़ सकते हैं।
अपराह्न

2
के लिए कोड की लगभग 2000 लाइनें graph.py --> class Graph। और सभी मैं देखना चाहता हूं कि वे कैसे उपयोग करते हैं __iter__
टी। वुडी

8

सबसे पहले, शास्त्रीय सूची बनाम मैट्रिक्स अभ्यावेदन का विकल्प उद्देश्य पर निर्भर करता है (आप प्रतिनिधित्व के साथ क्या करना चाहते हैं)। प्रसिद्ध समस्याओं और एल्गोरिदम पसंद से संबंधित हैं। अमूर्त प्रतिनिधित्व के प्रकार का विकल्प यह बताता है कि इसे कैसे लागू किया जाना चाहिए।

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

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

पायथन सूची को संदर्भों के एक गतिशील सरणी के रूप में लागू किया जाता है, पायथन टपल को स्थिर सामग्री के संदर्भ में स्थिर सरणी के रूप में लागू किया जाता है (संदर्भों के मूल्य को बदला नहीं जा सकता)। जिसकी वजह से उन्हें आसानी से अनुक्रमित किया जा सकता है। इस तरह, इस सूची का उपयोग मैट्रिसेस के कार्यान्वयन के लिए भी किया जा सकता है।

मैट्रिसेस का प्रतिनिधित्व करने का एक और तरीका मानक मॉड्यूल द्वारा कार्यान्वित सरणियाँ हैं array- संग्रहीत प्रकार, समरूपता मूल्य के संबंध में अधिक विवश। तत्व सीधे मूल्य को संग्रहीत करते हैं। (सूची इसके बजाय मान ऑब्जेक्ट के संदर्भ संग्रहीत करता है)। इस तरह, यह अधिक स्मृति कुशल है और मूल्य तक पहुंच भी तेज है।

कभी-कभी, आपको उपयोगी और भी अधिक प्रतिबंधित प्रतिनिधित्व मिल सकता है bytearray


7

दो उत्कृष्ट ग्राफ़ लाइब्रेरीज़ NetworkX और igraph हैं । आप GitHub पर दोनों पुस्तकालय स्रोत कोड पा सकते हैं। आप हमेशा देख सकते हैं कि फ़ंक्शन कैसे लिखे जाते हैं। लेकिन मैं NetworkX को पसंद करता हूं क्योंकि इसके समझने में आसान है।
यह जानने के लिए उनके कोड देखें कि वे कैसे कार्य करते हैं। आपको कई विचार मिलेंगे और फिर आप चुन सकते हैं कि आप डेटा संरचनाओं का उपयोग करके एक ग्राफ कैसे बनाना चाहते हैं।

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