问题描述
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 开头,则可以省略它。
一些所需的属性是:
如果各种算法在不同程度上满足这些要求,我很乐意看到所有这些算法的权衡。
欢迎使用任何语言或伪代码编写的算法。
示例图:
示例图 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
编辑:使用 DCG、SCC 和 topsort
使用@Cirdec 描述的算法:
(f, t)
表示 f
必须在之前t
在订购中。 condensation
1 中的 DCG。condensation
中的每个 SSC in 2.成回文。 在 haskell :
{-# 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
其中4
依赖于取决于。
最佳答案
我相信以下算法将在线性时间内找到最小的顶点串:
[1..n]
每个强连通分量按以下顺序[1..n] ++ [n-1..1]
关于algorithm - 允许重复的有向多重图的线性排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44319589/