给定顶点V
可以看作是命名"命题"。
给定权重:
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之后。
G = (V, E)
E = (V, V, W)
data W
= Requires -- ^ Denotes that a "proposition" depends on another.
| InvalidatedBy -- ^ Denotes that a "proposition" is invalidated by another.
鉴于顶点可能在排序中出现不止一次...... 如何从这样的图形构建线性排序?
此外,如果线性排序的尾部以顶点V结尾,该顶点V由于被无效而包含在另一个顶点中,那么如果排序的头部以V开头,则可以省略它。
一些理想的属性是:
如果各种算法在不同程度上满足这些要求,我很乐意看到所有这些算法的权衡。
欢迎使用任何语言或伪代码编写的算法。
示例图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]
一个天真的实现通过从没有传入边的所有节点开始构建线性排序,并且对于所有这些节点:
这是此描述的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
使用@Cirdec描述的算法:
给定有向循环图(DCG),其中格式:(f, t)
的边缘表示f
必须在排序中t
之前。
在1中计算DCG的condensation
。
将2. {2}中的每个SSC变成回文。
计算3中图表的topsort。
连接计算的排序。
在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
答案 0 :(得分:1)
我相信以下算法会在线性时间内找到最小的顶点字符串:
将图表分解为强连接组件。现有算法在线性时间内执行此操作。
在每个强连接组件中,每个节点都需要在每个其他节点之前和之后列出。按以下顺序列出每个强连接组件的节点[1..n]
[1..n] ++ [n-1..1]
通过拓扑排序将强连接组件按顺序连接在一起。现有算法在线性时间内拓扑排序这样的定向非线性图。