4

我有一个函数可以在一个范围内生成双打:

let gen_doublein = 
    fun mx mn -> Arb.generate<float> |> Gen.suchThat ( (>) mx ) |> Gen.suchThat ( (<) mn )

然后是一个生成其中 2 个数组的函数:

let gen_params:Gen<double array> = 
    gen { let! x = gen_doublein 0.0 20000.0
          let! y = gen_doublein 0.0 2000.0
          return [| x;y|] }

我放:

static member arb_params = Arb.fromGen  gen_params

在生成器类中并注册它。一切似乎都很好。为了测试这一切都可以,我有:

let f2 (xs:double array) :double= exp (-2.0*xs.[0]) + xs.[1]*exp (-2.0*xs.[0])
let fcheck fn xs = fn xs > 0.0

然后使用数组生成器'arrayOfLength':

Check.Quick (Prop.forAll (arrayOfLength 2) (fcheck f2))

可以按预期工作,但是:

Check.Quick (Prop.forAll (Generators.arb_params) (fcheck f2))

刚开始做一些计算,再也没有回来。f# 大师请帮忙。

4

3 回答 3

3

我没有尝试过,但我认为问题在于生成器float随机创建值,然后检查它们是否与您指定的谓词(范围)匹配。这意味着它必须在(随机)生成匹配的浮点数之前生成大量浮点数。

float通过在 [0 .. 1] 范围内生成值
然后重新缩放它们以匹配您需要的范围,在指定范围内生成值会更容易。

我对 FsCheck 不够熟悉,所以我不知道是否有 [0 .. 1] 浮点范围的生成器,但您可以从生成整数并将它们转换为浮点数开始:

let gen_doublein mx mn = gen {
   let! n = Arb.generate<int>
   let f = float n / float Int32.MaxValue 
   return mx + (f * (mn - mx)) }

编辑我看到你已经解决了这个问题。我认为我发布的解决方案可能仍然适用于较小的范围(随机生成器不能很快产生足够的匹配值)。

于 2012-03-20T16:09:25.323 回答
2

问题是参数是错误的。Tomas 的建议很好,并且有一些辅助函数可以实现它。

// Another id function
let fd (d:double) = d
// Check that it is in bounds
let mn=1.0
let mx=5.0
let fdcheck d = (fd d <= mx) && (fd d >= mn)
// Run the check with the numbers generated within the bounds
Check.Quick (Prop.forAll (Arb.fromGen (Gen.map (fun x->
                                                match x with 
                                                | _ when Double.IsNaN x -> (mn+mx)/2.0
                                                | _ when x>  1e+17 ->mx
                                                | _ when x< -1e17 ->mn
                                                | _ -> mn + (mx-mn)*(sin x+1.0)/2.0
                                            ) Arb.generate<double>
                                    )
                       ) fdcheck
         )

在这里,如果参数生成正确,我有一个函数可以通过测试。我不确定 Tomas 对整数的想法是否有效,因为我认为会生成很多小整数,因此双精度数不会过多地探索该域 - 但也许知道 FSCheck 的人可能会启发我们。

于 2012-03-21T11:51:22.687 回答
2

以这种方式重写了来自@b1g3ar5 的样本

let mapRangeNormal (min : float<_>, max : float<_>) x =
    match x with
    | _ when Double.IsNaN x -> (min + max) / 2.0
    | _ when Double.IsPositiveInfinity x -> max
    | _ when Double.IsNegativeInfinity x -> min
    | _ -> min + (max - min) * (sin x + 1.0) / 2.0

let mapRangeUniform (min : float<_>, max : float<_>) x =
    match x with
    | _ when Double.IsNaN x -> (min + max) / 2.0
    | _ when Double.IsPositiveInfinity x -> max
    | _ when Double.IsNegativeInfinity x -> min
    | _ when x < 0.0 ->
           let newRange = max - min
           min - x * (newRange / Double.MaxValue) - newRange / 2.0
    | _ -> let newRange = max - min
           min + x * (newRange / Double.MaxValue) + newRange / 2.0
于 2016-09-14T12:28:48.363 回答