2012-12-02 14 views
5

私は、エッジ(シングルエッジ)とエッジセット(エッジセット)を取る無向グラフのサイクルを検出するための次の手順があります。さらに2つの引数、left_set(再帰に渡すために必要な辺を格納することを意図している)と巡回(最終的にグラフが巡回したかどうかを決定するブール値です)。MySQL再帰的サイクル検出手順

何らかの理由で、最初の再帰を超えて動作しません。ここでは詳細を説明するコメントとコードは次のとおりです。

次の関数が(混乱を避けるために)MySQLでME BY実装されました:

-concat_set():返します置き換えられた2つのセットの連結 '、'空のセットの場合

-remove_first():これエッジこの'12のようになります「」:15' 、エッジ間の区切り文字であるエッジノードを戻します。設定

-get_left_node()/ get_right_nodeから最初のメンバーを削除

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

ループへの再帰を巻き戻すだけです。コードが少し醜いかもしれませんが、それはすでにかなり醜いです。 – siride

答えて

2

このmysqlの変数をリセットしてください:あなたのプロシージャを呼び出すコマンド上で実行した後

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

thread_stack max_sp_recursion_depth、。両方の文を逐次実行してください。 あなたの要件に応じてthread_stackのサイズを増やしてください。

+0

これは実際にはうまくいきましたが、私は前にこのような再帰深度を設定していました: 'SET GLOBAL max_sp_recursion_depth = 150; 「両者に違いはありますか?大きなセットのサイクルをチェックするときにも次のエラーが出ます: '#1436 - スレッドスタックオーバーラン'手動で大きなスタックを指定すべきですか、コードを修正してこれを防ぐ方が良いでしょうか? – Mike

+0

グローバルキーワードをリセットグローバルに変数をリセットすると、mysqlサービスが再起動するまで150がリセットされます。そして、この変数をグローバルに更新する必要はありません。その代わりに、セッションごとにその変数をリセットすることができます。 –

+0

私は、スレッドスタックオーバーランエラーを投げずに作業を掲示したコードは得られませんでした。 – Mike