依赖关系的有向多图的线性排序允许重复

时间:2017-06-02 01:44:16

标签: algorithm haskell graph topological-sort

问题描述

  1. 给定顶点V可以看作是命名"命题"。

  2. 给定权重:

  3. data W
      = Requires     -- ^ Denotes that a "proposition" depends on another.
      | Invalidates  -- ^ Denotes that a "proposition" invalidates another.
    

    在线性排序中,如果A需要B,则B必须在A之前,相反,如果A使B无效,则B必须在A之后。

    1. 给定一个加权有向多图(多重图),最多有2个平行边...其中一个顶点只需要包含另一个顶点一次,并且只会使另一个顶点失效一次......
    2. G = (V, E)
      E = (V, V, W)
      
      1. 或者可选地表示为没有自循环的有向循环图,其中唯一的循环直接在一个顶点和另一个顶点之间形成。权重更改为:
      2. data W
          = Requires      -- ^ Denotes that a "proposition" depends on another.
          | InvalidatedBy -- ^ Denotes that a "proposition" is invalidated by another.
        

        鉴于顶点可能在排序中出现不止一次...... 如何从这样的图形构建线性排序?

        此外,如果线性排序的尾部以顶点V结尾,该顶点V由于被无效而包含在另一个顶点中,那么如果排序的头部以V开头,则可以省略它。

        一些理想的属性是:

        1. 极简 - 应该尽可能少地重复顶点
        2. 稳定性 - 排序应尽可能与相同"级别上的顶点之间的顺序相似"其中构建了图表
        3. 运行时复杂性 - 顶点的数量并不高,但仍然......运行时复杂性应该尽可能低。
        4. 如果各种算法在不同程度上满足这些要求,我很乐意看到所有这些算法的权衡。

          欢迎使用任何语言或伪代码编写的算法。

          示例图:

          示例图1:

          B `requires`    A
          C `requires`    A
          D `requires`    A
          E `invalidates` A
          F `invalidates` A
          G `invalidates` A
          

          最小线性排序:[A,B,C,D,E,F,G]

          示例图2:

          C `requires`    A
          C `invalidates` A
          B `requires`    A
          

          最小线性排序:[A,B,C]

          示例图3:

          B `requires`    A
          B `invalidates` A
          C `requires`    A
          C `invalidates` A
          

          最小线性排序:[A,B,A,C]

          天真的实施

          一个天真的实现通过从没有传入边的所有节点开始构建线性排序,并且对于所有这些节点:

          1. 获取所有传出边
          2. 按需要/无效分区
          3. 构造"线性排序"并把它放在第一个
          4. 添加当前节点
          5. 构造"线性排序"无效"并补充说。
          6. 这是此描述的Haskell实现:

            import Data.List (partition)
            import Data.Maybe (fromJust)
            import Control.Arrow ((***))
            import Data.Graph.Inductive.Graph
            
            fboth :: Functor f => (a -> b) -> (f a, f a) -> (f b, f b)
            fboth f = fmap f *** fmap f
            
            outs :: Graph gr => gr a b -> Node -> (Adj b, a)
            outs gr n = let (_, _, l, o) = fromJust $ fst $ match n gr in (o, l)
            
            starts :: Graph gr => gr a b -> [(Adj b, a)]
            starts gr = filter (not . null . fst) $ outs gr <$> nodes gr
            
            partW :: Adj W -> (Adj W, Adj W)
            partW = partition ((Requires ==) . fst)
            
            linearize :: Graph gr => gr a W -> [a]
            linearize gr = concat $ linearize' gr <$> starts gr
            
            linearize' :: Graph gr => gr a W -> (Adj W, a) -> [a]
            linearize' gr (o, a) = concat req ++ [a] ++ concat inv
              where (req, inv) = fboth (linearize' gr . outs gr . snd) $ partW o
            

            然后可以通过删除相等的连续来优化排序:

            -- | Remove consecutive elements which are equal to a previous element.
            -- Runtime complexity: O(n), space: O(1)
            removeConsequtiveEq :: Eq a => [a] -> [a]
            removeConsequtiveEq = \case
              []    -> []
              [x]   -> [x]
              (h:t) -> h : ug h t
              where
                ug e = \case
                  []     -> []
                  (x:xs) | e == x    ->     ug x xs
                  (x:xs) | otherwise -> x : ug x xs
            

            编辑:使用DCG,SCC和topsort

            使用@Cirdec描述的算法:

            1. 给定有向循环图(DCG),其中格式:(f, t)的边缘表示f必须在排序中t之前。

            2. 在1中计算DCG的condensation

            3. 将2. {2}中的每个SSC变成回文。

            4. 计算3中图表的topsort。

            5. 连接计算的排序。

            6. 在Haskell:

              condensation

              图表{-# LANGUAGE LambdaCase #-} import Data.List (nub) import Data.Maybe (fromJust) import Data.Graph.Inductive.Graph import Data.Graph.Inductive.PatriciaTree import Data.Graph.Inductive.NodeMap import Data.Graph.Inductive.Query.DFS data MkEdge = MkEdge Bool Int Int req = MkEdge True inv = MkEdge False toGraph :: [MkEdge] -> [(Int, Int, Bool)] -> Gr Int Bool toGraph edges es = run_ empty nm where ns = nub $ edges >>= \(MkEdge _ f t) -> [f, t] nm = insMapNodesM ns >> insMapEdgesM es -- | Make graph into a directed cyclic graph (DCG). -- "Requires" denotes a forward edge. -- "Invalidates" denotes a backward edge. toDCG :: [MkEdge] -> Gr Int Bool toDCG edges = toGraph edges $ (\(MkEdge w f t) -> if w then (t, f, w) else (f, t, w)) <$> edges -- | Make a palindrome of the given list by computing: [1 .. n] ++ [n - 1 .. 1]. -- Runtime complexity: O(n). palindrome :: [a] -> [a] palindrome = \case [] -> [] xs -> xs ++ tail (reverse xs) linearize :: Gr Int a -> [Int] linearize dcg = concat $ topsort' scc2 where scc = nmap (fmap (fromJust . lab dcg)) $ condensation dcg scc2 = nmap palindrome scc

              g2

              由于排序违反了依赖关系,因此这种排序既不是最小的也不是有效的。 g2 = [ 2 `req` 1 , 2 `inv` 1 , 3 `req` 1 , 3 `inv` 1 , 4 `req` 1 , 5 `inv` 1 ] > prettyPrint $ toDCG g2 1:2->[(False,2)] 2:1->[(True,1),(True,3),(True,4)] 3:3->[(False,2)] 4:4->[] 5:5->[(False,2)] > prettyPrint $ condensation $ toDCG g2 1:[5]->[((),2)] 2:[1,2,3]->[((),3)] 3:[4]->[] > linearize $ toDCG g2 [5,2,1,3,1,2,4] 使5取决于1无效。 2会使2依赖的1无效。

              有效且最小的排序是:4。通过将列表向右移动,我们得到[1,4,2,1,3,5],这也是一个有效的排序。

              如果图表的方向被翻转,则排序变为:[5,1,4,2,1,3]。这不是有效排序......在边界处,[4,2,1,3,1,2,5]可能会发生,然后5,但4会使5取决于1

1 个答案:

答案 0 :(得分:1)

我相信以下算法会在线性时间内找到最小的顶点字符串:

  1. 将图表分解为强连接组件。现有算法在线性时间内执行此操作。

  2. 在每个强连接组件中,每个节点都需要在每个其他节点之前和之后列出。按以下顺序列出每个强连接组件的节点[1..n] [1..n] ++ [n-1..1]

  3. 通过拓扑排序将强连接组件按顺序连接在一起。现有算法在线性时间内拓扑排序这样的定向非线性图。