我写了一个算法来找到Haskell中子集求和问题的解决方案。签名是
subsetSum :: (Ord a, Num a) => [a] -> a -> Maybe [a]
QuickCheck似乎非常适合测试。例如,我在这里是我可以检查的属性之一:
prop_sumEqualsS l s = case subsetSum l s of
Just solution -> (sum solution) == s
Nothing -> True
问题是该算法计算量很大,运行100次大输入列表测试需要很长时间才能运行。
我尝试使用QuickCheck 1并且确实运行得很快,但用于测试的数据集非常小。转移到QuickCheck 2后,似乎是相反的问题。 QC有a manual,但它似乎很旧(没有日期信息),我不知道QC2还有多少适用。 A Tutorial可以在Haskell Wiki上找到,但没有太多细节,只有几个词就实例化Arbitrary
。
所以我有两个问题:
编辑:更具体一点,我想测试我的解决方案,列表大小从0到100,包含-10000到10000之间的整数。
答案 0 :(得分:25)
QuickCheck 2引入的一件事可能是其中的一个来源
效率低下是shrink
函数。如果财产失败,那么
调用缩小函数,使候选人变小
测试值,他们缩小,直到他们不能给
属性失败的较小值。但这只是
属性失败时会发生。
列表的任意实例的更改未更改
在version 1之间
和version 2。
区别在于措辞,版本1使用vector
,版本2使用
列表理解,然后vector
实现了这样的列表理解
对任意的有序调用。
两种实现都使用了size参数。在QuickCheck 1中,大小
生成的值是
默认div n 2 + 3
,其中n
是测试的编号。
QuickCheck 2使用另一种方法,即最大尺寸
并配置了测试次数。测试尺寸将分发
在该范围内,测试数量呈线性增长(参见computeSize'
中的quickCheckWithResult
)。
这意味着,默认设置为100次测试,最大尺寸为最大尺寸
来自QuickCheck 1的将是(div 100 2 + 3) = 53
,而使用QuickCheck 2
它只是100
。
由于子集和是NP完全的,你可能有一个指数算法;)
然后是长度为50和100的列表之间的运行时间差异
当然可以是巨大的。可以理解的是,您希望使用小型列表进行测试。你有两个
选择:创建新数据类型(最好使用newtype
)或make
一个独立的生成器并使用forAll
。使用newtype
即可
还指定了如何缩小值。这是使用newtype
方法的示例实现:
newtype SmallIntList = SmallIntList [Int] deriving (Eq,Show)
instance Arbitrary SmallIntList where
arbitrary = sized $ \s -> do
n <- choose (0,s `min` 50)
xs <- vectorOf n (choose (-10000,10000))
return (SmallIntList xs)
shrink (SmallIntList xs) = map SmallIntList (shrink xs)
此Arbitrary
实例不会使列表超过50个元素。
元素不使用size参数,因此它们总是在
范围[-10000,10000]
,因此还有一些改进空间。
shrink
函数只使用可用的shrink
列表和
Int
秒。
要将此实例与您的媒体资源一起使用,只需使用SmallIntList
即可
一个论点:
prop_sumEqualsS (SmallIntList l) s = case subsetSum l s of
...