6

我有兴趣学习一种优雅的方法来使用函数式编程语言中的柯里化来对多个积分进行数值评估。我选择的语言是 F#。

如果我想f(x,y,z)=8xyz在区域上积分,[0,1]x[0,1]x[0,1]我首先写下微分形式的三重积分8xyz dx dy dz。在某种意义上,这是三个有序参数的函数: a (float -> float -> float -> float)

4xy dx dy我取第一个积分,问题归结为on的二重积分[0,1]x[0,1]。从概念上讲,我们已经将函数柯里化为(float -> float -> float).

在第二个积分之后,我需要在单位区间上取2x dxa的积分。(float -> float)

在三个积分之后,我得到了结果,数字1.0

忽略数值积分的优化,我怎么能简洁地执行这个?我想写一些类似的东西:

let diffForm = (fun x y z -> 8 * x * y * z)

let result =
    diffForm
    |> Integrate 0.0 1.0
    |> Integrate 0.0 1.0
    |> Integrate 0.0 1.0

如果可能不切实际,这是可行的吗?我喜欢这样的想法,即这将在数学上捕捉到正在发生的事情。

4

2 回答 2

3

我喜欢这样的想法,即这将在数学上捕捉到正在发生的事情。

恐怕您的前提是错误的:管道运算符通过一系列函数将值线程化,并且与函数组合密切相关。然而,在n维域上积分类似于n嵌套循环,即在您的情况下类似于

for x in x_grid_nodes do
    for y in y_grid_nodes do
        for z in z_grid_nodes do
            integral <- integral + ... // details depend on integration scheme

您无法轻松地将其映射到对某个Integrate函数的三个独立调用链,因此组合integrate x1 x2 >> integrate y1 y2 >> integrate z1 z2实际上不是您在集成时所做的f。这就是为什么 Tomas 的解决方案——如果我理解正确(我对此不确定......)——本质上是在隐式定义的 3D 网格上评估您的函数并将其传递给集成函数。我怀疑这与您最初的问题一样接近。

您没有要求,但如果您确实想在实践中评估n维积分,请查看 Monte Carlo 积分,它避免了另一个通常称为“维度诅咒”的问题,即所需的数量使用经典积分方案,样本点随n呈指数增长。

更新

您可以实现迭代集成,但不能使用单个integrate函数,因为要集成的函数的类型对于集成的每个步骤都不同(即每个步骤将n元函数转换为 ( n - 1) 元函数):

let f = fun x y z -> 8.0 * x * y * z

// numerically integrate f on [x1, x2]
let trapRule f x1 x2 = (x2 - x1) * (f x1 + f x2) / 2.0 

// uniform step size for simplicity
let h = 0.1

// integrate an unary function f on a given discrete grid
let integrate grid f =
    let mutable integral = 0.0
    for x1, x2 in Seq.zip grid (Seq.skip 1 grid) do
        integral <- integral + trapRule f x1 x2
    integral

// integrate a 3-ary function f with respect to its last argument
let integrate3 lower upper f =
    let grid = seq { lower .. h .. upper }
    fun x y -> integrate grid (f x y)

// integrate a 2-ary function f with respect to its last argument
let integrate2 lower upper f =
    let grid = seq { lower .. h .. upper }
    fun x -> integrate grid (f x)

// integrate an unary function f on [lower, upper]
let integrate1 lower upper f =
    integrate (seq { lower .. h .. upper }) f

使用您的示例功能f

f |> integrate3 0.0 1.0 |> integrate2 0.0 1.0 |> integrate1 0.0 1.0

产生 1.0。

于 2013-01-17T20:08:03.877 回答
3

我不完全确定您将如何以正常方式实现此功能,因此这可能无法完全解决问题,但这里有一些想法。

要进行数值积分,您将(我认为?)需要在管道中调用diffForm指定的各个点调用原始函数Integrate- 但实际上您需要在范围的乘积处调用它 - 所以如果我想要要仅在边界处调用它,我仍然需要调用 2x2x2 次以涵盖所有可能的组合(diffForm 0 0 0diffForm 0 0 1diffForm 0 1 0),然后对您获得的 8 个结果进行一些计算。

以下示例(至少)显示了如何编写类似的代码来调用具有您指定的参数值的所有组合的指定函数。

这个想法是使用可以多次调用的延续(所以当我们得到一个函数时,我们可以在多个不同的点重复调用它)。

// Our original function
let diffForm x y z = 8.0 * x * y * z

// At the first step, we just pass the function to a continuation 'k' (once)
let diffFormK k = k diffForm

// This function takes a function that returns function via a continuation
// (like diffFormK) and it fixes the first argument of the function
// to 'lo' and 'hi' and calls its own continuation with both options
let range lo hi func k = 
  // When called for the first time, 'f' will be your 'diffForm'
  // and here we call it twice with 'lo' and 'hi' and pass the 
  // two results (float -> float -> float) to the next in the pipeline
  func (fun f -> k (f lo)) 
  func (fun f -> k (f hi)) 

// At the end, we end up with a function that takes a continuation
// and it calls the continuation with all combinations of results
// (This is where you need to do something tricky to aggregate the results :-))
let integrate result =
  result (printfn "%f")

// Now, we pass our function to 'range' for every argument and
// then pass the result to 'integrate' which just prints all results
let result =
    diffFormK
    |> range 0.0 1.0
    |> range 0.0 1.0
    |> range 0.0 1.0
    |> integrate

这可能很令人困惑(因为延续需要很长时间才能习惯),但也许您(或这里的其他人?)可以找到一种方法将第一次尝试变成真正的数值积分:-)

于 2013-01-17T00:56:26.343 回答