使用 SBV 库,我试图满足状态符号列表的条件:
data State = Intro | Start | Content | Comma | Dot
mkSymbolicEnumeration ''State
-- examples of such lists
[Intro, Start, Content, Comma, Start, Comma, Content, Dot]
[Intro, Comma, Start, Content, Comma, Content, Start, Dot]
一切正常,除了我需要最终列表完全包含n
其中任何一个的元素[Intro, Start, Content]
。目前我使用有界过滤器来做到这一点:
answer :: Int -> Symbolic [State]
answer n = do
seq <- sList "seq"
let maxl = n+6
let minl = n+2
constrain $ L.length seq .<= fromIntegral maxl
constrain $ L.length seq .>= fromIntegral minl
-- some additional constraints hidden for brevity purposes
let etypes e = e `sElem` [sIntro, sStart, sContent]
constrain $ L.length (L.bfilter maxl etypes seq) .== fromIntegral n
如您所见,列表可以是 和 之间的任意长度n+2
,n+6
重要的是其中包含正确数量的[sIntro, sStart, sContent]
元素。
它工作得很好,除了它非常慢。就像,因为n=4
它需要几秒钟,但n>=6
它需要永远(超过 30 分钟并且还在计数)。如果我删除有界过滤器约束,结果是即时的,n
最多 25 个左右。
最后,我并不特别关心使用L.bfilter
. 我所需要的只是一种声明最终符号列表应该包含某些 n
给定类型的元素的方法。
->有没有更快的方法可以满足count(sIntro || sStart || sContent)
?
-在评论中讨论后编辑:
下面的代码应该确保所有有效元素都在elts
列表的前面。例如,如果我们valids
从 中计算 8 个元素elts
,那么我们take 8 elts
将计算validTaken
此子列表中的有效元素。如果结果是8
,则意味着所有 8valids
个元素都在elts
. Unsat
可悲的是,即使在消除了所有其他约束之后,这也会产生系统性的结果。但是,当针对一些虚拟元素列表进行测试时,该函数运行良好。
-- | test that all valid elements are upfront in the list of elements
validUpFront :: SInteger -> [Elem] -> SBool
validUpFront valids elts =
let takeValids = flip take elts <$> (fromInteger <$> unliteral valids)
validTaken = sum $ map (oneIf . included) $ fromMaybe [] takeValids
in valids .== validTaken
-- ...
answer n = runSMT $ do
-- ...
let valids = sum $ map (oneIf . included) elts :: SInteger
constrain $ validUpFront valids elts