2012-12-02 18 views
5

Mam następującą procedurę, która ma na celu wykrycie cykli na nieukierunkowanym wykresie przyjmującym krawędź (singleedge) i zestaw krawędzi (zestaw krawędziowy). Istnieją jeszcze dwa argumenty: left_set (który ma na celu zapisanie niezbędnych krawędzi do przekazania do rekurencji) i cykliczny (który jest wartością logiczną, która ostatecznie określa, czy wykres był cykliczny, czy nie,Procedura wykrywania cyklu rekurencyjnego MySQL

Z jakiegoś powodu wykrywanie nie działa po pierwszym rekursji Oto kod z komentarzy wyjaśniając szczegóły.

następujące funkcje zostały wprowadzone przeze mnie w MySQL (aby uniknąć nieporozumień):

-concat_set(): zwraca konkatenacja dwóch zestawów odpowiadających błędnemu umieszczeniu "," w przypadku pustego zestawu

-remove_first() usuwa się pierwszy element z zestawu

-get_left_node()/get_right_node: powraca węzły krawędzi separatory brzegów „” więc krawędź wygląda następująco '12: 15'

CREATE PROCEDURE `is_cyclic`(
IN `singleedge` VARCHAR(15), 
IN `edgeset` VARCHAR(1024), 
IN 'left_set' VARCHAR(512), 
OUT `cyclic` BOOLEAN) 

BEGIN 
DECLARE se_left VARCHAR(5); 
DECLARE es_left VARCHAR(5); 
DECLARE se_right VARCHAR(5); 
DECLARE es_right VARCHAR(5); 
Call get_left_node(singleedge, se_left); 
Call get_left_node(SUBSTRING_INDEX(edgeset, ',', 1), es_left); 
Call get_right_node(singleedge, se_right); 
Call get_right_node(SUBSTRING_INDEX(edgeset, ',', 1), es_right); 



--is edgeset emptY? 
    IF LENGTH(edgeset)= 0 AND LENGTH(left_set) = 0 THEN 
     BEGIN 

      SET cyclic= false; 

     END;  

--are singeeledge and first edge in edgeset the same?   
    ELSEIF ((se_left = es_left 
     OR se_left= es_right) 
     AND(se_right = es_left 
     OR se_right = es_right)) THEN 
        BEGIN 
      set cyclic= true; 
         END; 


--do singleedge and first edge in edgeset share any vertices?  
    ELSEIF se_left = es_left 
     OR se_left= es_right 
     OR se_right = es_left 
     OR se_right = es_right 
     THEN 
     --check for all possiblities 
      BEGIN 

       --if left vertex of singleedge and left vertex of current edge in front of edgeset are the same    
       IF se_left=es_left THEN 
            BEGIN 
            --test if the edge of combined uncommon vertices (forming concat(se_right,':',es_right)) exists in the remaining edgeset concatanated with the left_set 
            CALL is_cyclic(concat(se_right,':',es_right),concat_set(left_set,remove_first(edgeset)), '', cyclic); 
            --if the recursion returns cyclic=false, then remove the considered edge from edgeset and continue trying to match the original singleedge with the rest of edgeset 
            IF cyclic=false THEN 
             CALL is_cyclic(singleedge, remove_first(edgeset), left_set, cyclic); 
             END IF; 
            END; 
       ELSEIF se_left= es_right THEN 
            BEGIN 
            CALL is_cyclic(concat(se_right,':',es_left), concat_set(left_set, remove_first(edgeset)), '', cyclic); 
            IF cyclic=false THEN 
             CALL is_cyclic(singleedge, remove_first(edgeset), left_set, cyclic); 
            END IF; 
            END; 
       ELSEIF se_right=es_left THEN 
            BEGIN 
            CALL is_cyclic(concat(se_left,':',es_right), concat_set(left_set, remove_first(edgeset)), '', cyclic); 
            IF cyclic=false THEN 
             CALL is_cyclic(singleedge, remove_first(edgeset), left_set, cyclic); 
            END IF; 
            END; 
       ELSE 
            BEGIN 
            CALL is_cyclic(concat(se_left,':',es_left), concat_set(left_set, remove_first(edgeset)), '', cyclic); 
            IF cyclic=false THEN 
             CALL is_cyclic(singleedge, remove_first(edgeset), left_set, cyclic); 
            END IF; 
             END; 
           END IF; 


      END;  


     ELSE 
      BEGIN 
       --if the current edge being considered from the edgeset does not contain any vertex in common with singleedge, set it aside into left_set and call is_cyclic recurisvely with the edge removed 
       SET left_set = concat_set(left_set, SUBSTRING_INDEX(edgeset, ',', 1)); 
       CALL is_cyclic(singleedge, remove_first(edgeset), left_set, cyclic); 
       END; 

    END IF; 
END 
+1

Właśnie odprężyłbym rekurencję w pętli. To może uczynić kod trochę brzydszym, ale jest już dość brzydki. – siride

Odpowiedz

2

Spróbuj zresetować zmienną mysql: max_sp_recursion_depth, thread_stack

SET SESSION max_sp_recursion_depth = 10; 
SET SESSION thread_stack = 250000; 

Po wykonaniu powyższego polecenia wywołać swoją procedurę. Wykonaj obie instrukcje szeregowo. Zwiększ wielkość zmiennej thread_stack zgodnie z wymaganiami.

+0

To zadziałało, ustawiałem głębokość rekursji w ten sposób: 'SET GLOBAL max_sp_recursion_depth = 150; "czy jest jakaś różnica między tymi dwoma? Otrzymuję również następujący błąd podczas sprawdzania cykli dla większych zestawów: '# 1436 - Przekroczony stos wątków' Czy powinienem ręcznie określić większy stos, czy lepiej jest naprawić kod i temu zapobiec? – Mike

+0

Globalne słowo kluczowe resetujące zmienną globalnie oznacza, że ​​resetuje się o 150, dopóki usługa mysql nie zostanie ponownie uruchomiona. Nie jest konieczne, aby globalnie zmienić tę zmienną, dlatego zamiast tego można zresetować tę zmienną na sesję. –

+0

Nigdy nie otrzymałem kodu, który opublikowałem, działając bez błędu "przekroczenia stosu wątków". – Mike

Powiązane problemy