[英]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 的情况。
我想要的是:
我认为我有什么:
我怎样才能解决这个问题?
您已经正确识别了问题:您生成的是具有属性∃ 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.