重新排序搜索空间

时间:2017-09-15 12:36:23

标签: haskell search

我试图找到在降序排序的搜索空间中第一次出现,它满足某个谓词。

选择这种策略是因为计算谓词可能非常昂贵,并且在前者中找到解决方案的概率非常高。

这是解决方案,首先构建所有可能解决方案的列表,然后安排并生成线性搜索。

import Data.Ord
import Data.List

search :: (Ord a, Num a) => ([a] -> Bool) -> [[a]] -> Maybe [a]
search p = find p . sortOn (Down . sum) . sequence

实施例

main = print $ search ((<25) . sum) [[10,2], [10,8,6], [8]]

输出

Just [10,6,8]

问题

有没有办法按降序生成此空间的元素而不进行排序?

1 个答案:

答案 0 :(得分:4)

描述的确切案例

在这个确切的情况下,空间中有一个明确的最佳元素,如果任何元素与谓词匹配,那么最好的元素就是:

-- I have, over the years, found many uses for ensure
ensure p x = x <$ guard p x
search p = ensure p . map minimum

(<25) . sum是一个占位符,但Down . sum确切地说是

如果您的谓词只是一个示例,但您的启发式实际上是求和,则可以使用优先级队列来搜索空间。为简单起见,我将使用[(b,a)]作为优先级b和值a的优先级队列,保持列表按b排序的不变量。当然,如果你想要效率,你应该使用更好的实现。

现在我们基本上只是重新实现sequence以按优先级顺序生成其元素,并将其生成的列表的总和保持为优先级。引入优先级队列不变量是一个小的一次性成本。

import Data.List
import Data.Ord

increasingSums :: (Ord a, Num a) => [[a]] -> [[a]]
increasingSums = map snd . go . map sort where
    go [] = [(0,[])]
    go (xs:xss) = let recurse = go xss in mergeOn fst
        [ [ (total+h, h:ts)
          | (total, ts) <- recurse
          ]
        | h <- xs
        ]

唯一缺少的是mergeOn,它将一组优先级队列展平为一个:

mergeOn :: Ord b => (a -> b) -> [[a]] -> [a]
mergeOn f = go . sortOn (f . head) . filter (not . null) where
    go [] = []
    go ([x]:xss) = x : go xss
    go ((x:xs):xss) = x : go (insertBy (comparing (f . head)) xs xss)

ghci中进行测试,我们可以看到此表达式在非愚蠢的时间内完成:

> take 10 . increasingSums . replicate 4 $ [1..1000]
[[1,1,1,1],[2,1,1,1],[1,2,1,1],[1,1,2,1],[1,1,1,2],[2,1,1,2],[1,2,1,2],[1,1,2,2],[1,1,1,3],[2,1,2,1]]

而这个表达式不是:

> take 10 . sortOn sum . sequence . replicate 4 $ [1..1000]
^C^C^C^COMG how do I quit

同时,按排序顺序生成完整的总和列表也很有竞争力(至少在编译之前,我没有测试优化版本是否也大致相同):

> :set +s
> sum . map sum . increasingSums . replicate 4 $ [1..30]
50220000
(1.99 secs, 1,066,135,432 bytes)
> sum . map sum . sortOn sum . sequence . replicate 4 $ [1..30]
50220000
(2.60 secs, 2,226,497,344 bytes)

Down . sum是占位符

最后,如果你的启发式只是一个例子,并且你想要一个适用于所有启发式的完全通用的解决方案,那你就不走运了。在搜索空间中进行结构化漫游需要了解有关该结构的特殊内容。 (例如,上面我们知道如果x<y然后total+x<total+y,我们利用它来便宜地维护我们的优先级队列。)