繁体   English   中英

使用 QuickCheck 生成单射函数?

[英]Generate injective functions with QuickCheck?

我正在使用 QuickCheck 生成任意函数,并且我想生成任意单函数(即fa == fb当且仅当a == b时)。

以为我已经弄清楚了:

newtype Injective = Injective (Fun Word Char) deriving Show

instance Arbitrary Injective where
  arbitrary = fmap Injective fun
    where
      fun :: Gen (Fun Word Char)
      fun = do
        a <- arbitrary
        b <- arbitrary
        arbitrary `suchThat` \(Fn f) ->
          (f a /= f b) || (a == b)

但是我看到生成的 function 将不同的输入映射到相同的 output 的情况。

我想要的是:

  • f使得对于所有输入ab ,要么f a不等于f b要么a等于b

我认为我有什么:

  • f使得存在输入ab ,其中f a不等于f ba等于b

我怎样才能解决这个问题?

您已经正确识别了问题:您生成的是具有属性∃ a≠b. fa≠fb ∃ a≠b. fa≠fb (无论如何,这对于大多数随机函数来说都是正确的),而您想要的是∀ a≠b. fa≠fb ∀ a≠b. fa≠fb 这是一个更难确保的属性,因为您需要了解所有其他 function 值以生成每个单独的值。

我不认为这可以确保一般输入类型,但是对于单词,你可以做的是“假” function 通过按顺序预先计算所有 output 值,确保你不重复已经完成,然后从该预定图表中读取。 它需要一点懒惰才能真正让这个工作:

import qualified Data.Set as Set

newtype Injective = Injective ([Char] {- simply a list without duplicates -})
 deriving Show

instance Arbitrary Injective where
  arbitrary = Injective . lazyNub <$> arbitrary

lazyNub :: Ord a => [a] -> [a]
lazyNub = go Set.empty
 where go _ [] = []
       go forbidden (x:xs)
        | x `Set.member` forbidden  = go forbidden xs
        | otherwise                 = x : go (Set.insert x forbidden) xs

这不是很有效,并且可能不适合您的应用程序,但这可能是您能做的最好的事情。

在实践中,要实际使用Injective作为 function,您需要将值包装在一个合适的结构中,该结构只有O (log n ) 查找时间。 不幸的是, Data.Map.Lazy不够懒惰,您可能需要手工烘焙类似指数增长地图列表的东西。

还有人担心,对于一些不够大的结果类型,由于没有足够的可用值,因此不可能生成单射函数。 事实上,正如约瑟夫所说,这里就是这种情况。 在这种情况下, lazyNub function 将 go 进入无限循环。 我会说,对于 QuickCheck,这可能还可以。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM