6

関数型プログラミング言語でカリー化を使用して多重積分を数値的に評価するエレガントな方法を学ぶことに興味があります。私が選んだ言語はF#です。

f(x,y,z)=8xyz領域で積分したい場合[0,1]x[0,1]x[0,1]は、微分形式の三重積分を書き留めることから始め8xyz dx dy dzます。ある意味で、これは3つの順序付けられた引数の関数です(float -> float -> float -> float)

私は最初の積分を取り、問題は4xy dx dyonの二重積分に還元され[0,1]x[0,1]ます。概念的には、関数をカレーして。になります(float -> float -> float)

2番目の積分の後、単位間隔で2x dx、aの積分を取るように残されています。(float -> float)

3つの積分の後、結果、数が残り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

これを通常の方法で実装する方法が完全にはわからないため、問題を完全に解決できない可能性がありますが、ここにいくつかのアイデアがあります。

diffForm数値積分を行うには、パイプラインの呼び出しで指定されたさまざまなポイントで元の関数を呼び出すIntegrate必要がありますが、実際には範囲の積で呼び出す必要があります。境界でのみ呼び出すには、可能なすべての組み合わせ(、diffForm 0 0 0など)をカバーするために2x2x2回呼び出す必要があります。次に、得られた8つの結果について計算を行います。diffForm 0 0 1diffForm 0 1 0

次のサンプルは、(少なくとも)指定した引数値のすべての組み合わせで指定された関数を呼び出す同様のコードを作成する方法を示しています。

アイデアは、複数回呼び出すことができる継続を使用することです(したがって、関数を取得すると、複数の異なるポイントで繰り返し呼び出すことができます)。

// 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 に答える
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これを、ある関数への3つの独立した呼び出しのチェーンに簡単にマッピングすることはできませんintegrate x1 x2 >> integrate y1 y2 >> integrate z1 z2したがって、実際には、統合するときに構成が実行されることはありません。そのため、Tomasのソリューションは、正しく理解していれば(そして、それについてはよくわかりませんが...)、基本的に、暗黙的に定義された3Dグリッドで関数を評価し、それを積分関数に渡します。私はあなたがあなたの元の質問に到達することができるのと同じくらい近いと思います。f

あなたはそれを求めませんでしたが、実際にn次元積分を評価したい場合は、モンテカルロ積分を調べてください。これにより、一般に「次元の呪い」として知られる別の問題、つまり、必要な数が必要になるという事実を回避できます。サンプルポイントは、従来の積分スキームではnとともに指数関数的に増加します。

アップデート

integrate積分の各ステップで積分される関数のタイプが異なるため、反復積分を実装できますが、単一の関数では実装できません(つまり、各ステップでn - ary関数が(n-1)-ary関数に変わります。 )::

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 に答える