2010-02-18 14 views
10

है, मैं आपकी सुविधा के लिए यहां पुन: उत्पन्न होने वाले तारजन के दृढ़ता से जुड़े घटकों (एससीसी) के एक पुनरावृत्ति संस्करण को लागू करने की कोशिश कर रहा हूं (स्रोत: http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm)।एक रिकर्सिव एल्गोरिदम का इटरेटिव संस्करण धीमा

Input: Graph G = (V, E) 

index = 0       // DFS node number counter 
S = empty       // An empty stack of nodes 
forall v in V do 
    if (v.index is undefined)  // Start a DFS at each node 
    tarjan(v)      // we haven't visited yet 

procedure tarjan(v) 
    v.index = index     // Set the depth index for v 
    v.lowlink = index 
    index = index + 1 
    S.push(v)      // Push v on the stack 
    forall (v, v') in E do   // Consider successors of v 
    if (v'.index is undefined) // Was successor v' visited? 
     tarjan(v')    // Recurse 
     v.lowlink = min(v.lowlink, v'.lowlink) 
    else if (v' is in S)   // Was successor v' in stack S? 
     v.lowlink = min(v.lowlink, v'.lowlink) 
    if (v.lowlink == v.index)  // Is v the root of an SCC? 
    print "SCC:" 
    repeat 
     v' = S.pop 
     print v' 
    until (v' == v) 

मेरा पुनरावृत्ति संस्करण निम्न नोड संरचना का उपयोग करता है।

struct Node { 
    int id; //Signed int up to 2^31 - 1 = 2,147,483,647 
    int index; 
    int lowlink;   
    Node *caller;     //If you were looking at the recursive version, this is the node before the recursive call 
    unsigned int vindex;    //Equivalent to the iterator in the for-loop in tarjan 
    vector<Node *> *nodeVector;  //Vector of adjacent Nodes 
}; 

यहाँ है कि मैं क्या पुनरावृत्ति संस्करण के लिए किया गया है:

void Graph::runTarjan(int out[]) { //You can ignore out. It's a 5-element array that keeps track of the largest 5 SCCs 
     int index = 0; 
tarStack = new stack<Node *>(); 
    onStack = new bool[numNodes]; 
    for (int n = 0; n < numNodes; n++) { 
    if (nodes[n].index == unvisited) { 
     tarjan_iter(&nodes[n], index); 
    } 
    } 
} 

void Graph::tarjan_iter(Node *u, int &index) { 
    u->index = index; 
    u->lowlink = index; 
    index++; 
    u->vindex = 0; 
    tarStack->push(u); 
    u->caller = NULL;   //Equivalent to the node from which the recursive call would spawn. 
    onStack[u->id - 1] = true; 
    Node *last = u; 
    while(true) { 
     if(last->vindex < last->nodeVector->size()) {  //Equivalent to the check in the for-loop in the recursive version 
      Node *w = (*(last->nodeVector))[last->vindex]; 
      last->vindex++;         //Equivalent to incrementing the iterator in the for-loop in the recursive version 
      if(w->index == unvisited) { 
       w->caller = last;      
       w->vindex = 0; 
       w->index = index; 
       w->lowlink = index; 
       index++; 
       tarStack->push(w); 
       onStack[w->id - 1] = true; 
       last = w; 
      } else if(onStack[w->id - 1] == true) { 
       last->lowlink = min(last->lowlink, w->index); 
      } 
     } else { //Equivalent to the nodeSet iterator pointing to end() 
      if(last->lowlink == last->index) { 
       numScc++; 
       Node *top = tarStack->top(); 
       tarStack->pop(); 
       onStack[top->id - 1] = false; 
       int size = 1; 

       while(top->id != last->id) { 
        top = tarStack->top(); 
        tarStack->pop(); 
        onStack[top->id - 1] = false; 
        size++; 
       } 
       insertNewSCC(size); //Ranks the size among array of 5 elements 
      } 

      Node *newLast = last->caller; //Go up one recursive call 
      if(newLast != NULL) { 
       newLast->lowlink = min(newLast->lowlink, last->lowlink); 
       last = newLast; 
      } else { //We've seen all the nodes 
       break; 
      } 
     } 
    } 
} 

मेरे पुनरावृत्ति संस्करण रन और मुझे पुनरावर्ती संस्करण के समान उत्पादन देता है। समस्या यह है कि पुनरावृत्त संस्करण धीमा है, और मुझे यकीन नहीं है कि क्यों। क्या कोई मुझे मेरे कार्यान्वयन पर कुछ अंतर्दृष्टि दे सकता है? क्या पुनरावर्ती एल्गोरिदम को प्रभावी रूप से कार्यान्वित करने का कोई बेहतर तरीका है?

+7

क्या आप सेमी-स्यूडोकोड के बजाय वास्तविक कोड पोस्ट कर सकते हैं? यह शायद एक कार्यान्वयन मुद्दा है। ऐसा लगता है कि आप बहुत सारी अनावश्यक प्रतियां कर रहे हैं, लेकिन मैं यह नहीं बता सकता क्योंकि आपने अपना वास्तविक कोड छद्म कोड में बदल दिया है। –

+0

मैंने आपके अनुरोध के अनुसार वास्तविक कोड जोड़ा है! :) – user5243421

+0

कितना धीमा? –

उत्तर

13

एक रिकर्सिव एल्गोरिदम स्टोरेज क्षेत्र के रूप में ढेर का उपयोग करता है। पुनरावृत्त संस्करण में, आप कुछ वैक्टरों का उपयोग करते हैं, जो खुद ही ढेर आवंटन पर भरोसा करते हैं। स्टैक-आधारित आवंटन बहुत तेज़ माना जाता है, क्योंकि यह केवल एंड-स्टैक पॉइंटर को स्थानांतरित करने का मामला है, जबकि ढेर आवंटन काफी धीमा हो सकता है। यह पुनरावृत्ति संस्करण धीमा है पूरी तरह से आश्चर्यजनक नहीं है।

आम तौर पर, अगर हाथ में समस्या एक स्टैक-केवल रिकर्सिव मॉडल के भीतर अच्छी तरह से फिट बैठती है, तो, हर तरह से, रिकर्स करें।

+7

+1। बस सुनिश्चित करें कि यह ढेर पर फिट बैठता है :) –

+0

@mathee, यदि आप मनमानी डेटा आकार का समर्थन करना चाहते हैं, तो आप अंततः सीमा को हिट करेंगे। इस प्रकार टिप्पणियां ला ला * "अगर यह ढेर पर फिट बैठती है" *। –

+1

ऐसा लगता है कि आपके पुनरावर्तक कोड "कृत्रिम ढेर" का उपयोग करता है। इस तरह यह रिकर्सिव एल्गोरिदम के रूप में एक ही कम्प्यूटेशनल जटिलता और स्मृति सहक्रिया होना चाहिए। इसे अनुकूलित करने के लिए, यदि कोई अप्रत्याशित हॉटस्पॉट है तो यह पता लगाने के लिए मैं एक पारंपरिक प्रोफाइलिंग दृष्टिकोण का उपयोग करूंगा। – Chromatix

संबंधित मुद्दे