Frage

messing Nur etwa mit F # und ich versuche, eine grundlegende Lagrange-Interpolation-Funktion zu erstellen, basierend auf diesem C # -Version (kopiert von einem C ++ Wiki-Eintrag):

    double Lagrange(double[] pos, double[] val, double desiredPos)
    {
        double retVal = 0;

        for (int i = 0; i < val.Length; ++i)
        {
            double weight = 1;

            for (int j = 0; j < val.Length; ++j)
            {
                // The i-th term has to be skipped
                if (j != i)
                {
                    weight *= (desiredPos - pos[j]) / (pos[i] - pos[j]);
                }
            }

            retVal += weight * val[i];
        }

        return retVal;
    }

Das Beste, was ich mit der Verwendung meiner begrenzte Kenntnis von F # und funktionaler Programmierung einfiel war:

let rec GetWeight desiredPos i j (pos : float[]) weight = 
   match i with
   | i when j = pos.Length -> weight
   | i when i = j -> GetWeight desiredPos i (j+1) pos weight 
   | i -> GetWeight desiredPos i (j+1) pos (weight * (desiredPos - pos.[j])/(pos.[i] - pos.[j]) ) 

let rec Lagrange (pos : float[]) (vals : float[]) desiredPos result counter = 
   match counter with
   | counter when counter = pos.Length -> result
   | counter -> Lagrange pos vals desiredPos (result + (GetWeight desiredPos counter 0 pos 1.0)* vals.[counter]) (counter+1)

Kann jemand eine bessere / aufgeräumter F # -Version basiert auf der gleichen C # -Code?

War es hilfreich?

Lösung

Umfalten Sequenzen ist ein üblicher Weg, Schleifen mit einem Akkumulator zu ersetzen.

let Lagrange(pos:_[], v:_[], desiredPos) =
  seq {0 .. v.Length-1} 
  |> Seq.fold (fun retVal i -> 
      seq {for j in 0 .. pos.Length-1 do if i <> j then yield j} 
      |> Seq.fold (fun w j -> w * (desiredPos - pos.[j]) / (pos.[i] - pos.[j])) 1.0
      |> (fun weight -> weight * v.[i] + retVal)) 0.0

Andere Tipps

Der Teil, der Ihre funktionale Lösung hässlich macht, ist das Überspringen der i-ten Element, die Indizes bedeutet. Ziehen Sie, dass in eine wiederverwendbare Funktion aus, so dass alle hässlichen Index Handhabung isoliert ist. Ich nenne meinen Roundrobin.

let RoundRobin l = seq {
  for i in {0..Seq.length l - 1} do
    yield (Seq.nth i l, Seq.take i l |> Seq.append <| Seq.skip (i+1) l)
}

Es könnte viel hässlicher sein, wenn Sie eine effiziente Version produzieren wollen, though.

ich nicht product im Seq-Modul finden kann, so schrieb ich meine eigenen.

let prod (l : seq<float>) = Seq.reduce (*) l

Sie nun den Code Herstellung ist relativ einfach:

let Lagrange pos value desiredPos = Seq.sum (seq {
  for (v,(p,rest)) in Seq.zip value (RoundRobin pos) do
    yield v * prod (seq { for p' in rest do yield (desiredPos - p') / (p - p') })
})

Roundrobin stellt sicher, dass pos [i] nicht mit dem Rest des POS in der inneren Schleife enthalten. Um die val Array zu umfassen, ich den Reißverschluss mit dem Rund robinned pos Array.

Die Lektion ist hier, dass die Indizierung in einem funktionalen Stil sehr hässlich. Auch entdeckte ich ein cooler Trick: |> Seq.append <| gibt Ihnen Infix Syntax zum Anhängen von Sequenzen. Nicht ganz so schön wie ^ though.

Ich denke, das fein wie zwingend notwendig Code funktioniert:

let LagrangeI(pos:_[], v:_[], desiredPos) =
    let mutable retVal = 0.0
    for i in 0..v.Length-1 do
        let mutable weight = 1.0
        for j in 0..pos.Length-1 do
            // The i-th term has to be skipped
            if j <> i then
                weight <- weight * (desiredPos - pos.[j]) / (pos.[i] - pos.[j])
        retVal <- retVal + weight * v.[i]
    retVal

, aber wenn Sie funktionelle wollen, einige Falten (zusammen mit mapi, da Sie oft die Indizes entlang tragen müssen) gut funktionieren:

let LagrangeF(pos:_[], v:_[], desiredPos) =
    v |> Seq.mapi (fun i x -> i, x)
      |> Seq.fold (fun retVal (i,vi) ->
        let weight = 
            pos |> Seq.mapi (fun j x -> j<>i, x) 
                |> Seq.fold (fun weight (ok, posj) ->
                    if ok then
                        weight * (desiredPos - posj) / (pos.[i] - posj)
                    else
                        weight) 1.0
        retVal + weight * vi) 0.0

Ich weiß nicht, die Mathematik hier, so habe ich einige zufällige Werte zu testen (hoffentlich) zu gewährleisten, schraubte ich nichts auf:

let pos = [| 1.0; 2.0; 3.0 |]
let v = [|8.0; 4.0; 9.0 |]

printfn "%f" (LagrangeI(pos, v, 2.5))  // 5.375
printfn "%f" (LagrangeF(pos, v, 2.5))  // 5.375

Hier ist eine nicht-rekursive Lösung. Es ist ein bisschen flippig, da der Algorithmus Indizes erfordert, aber hoffentlich zeigt es, wie F # 's Funktionen zusammengesetzt sein kann:

let Lagrange (pos : float[]) (vals : float[]) desiredPos = 
    let weight pos desiredPos (i,v) =
        let w = pos |> Array.mapi (fun j p -> j,p)
                    |> Array.filter (fun (j,p) -> i <> j)
                    |> Array.fold (fun acc (j,p) -> acc * (desiredPos - p)/(pos.[i] - p)) 1.
        w * v
    vals |> Array.mapi (fun i v -> i,v)
         |> Array.sumBy (weight pos desiredPos)
            let rec GetWeight desiredPos i j (pos : float[]) weight = 
               if j = pos.Length then weight
               elif i = j then GetWeight desiredPos i (j+1) pos weight 
               else GetWeight desiredPos i (j+1) pos (weight * (desiredPos - pos.[j])/(pos.[i] - pos.[j]) ) 

            let rec Lagrange (pos : float[]) (vals : float[]) desiredPos result counter = 
               if counter = pos.Length then result
               else Lagrange pos vals desiredPos (result + (GetWeight desiredPos counter 0 pos 1.0)* vals.[counter]) (counter+1)

Ich persönlich denke, dass einfach, wenn / elif / else-Konstrukte hier viel besser ohne solche Gemeinkosten wie

match i with   
|i when i=...

Wenn Sie verwirren nur um dann hier eine Version zu Brians ähnlich, die Funktion currying verwendet und den Tupel Rohr Operator.

let Lagrange(pos:_[], v:_[], desiredPos) =
    let foldi f state = Seq.mapi (fun i x -> i, x) >> Seq.fold f state
    (0.0, v) ||> foldi (fun retVal (i, posi) -> 
        (1.0, pos) ||> foldi (fun weight (j, posj) -> 
            if j <> i then
                (desiredPos - posj) / (posi - posj)
            else
                1.0)
        |> (fun weight -> weight * posi + retVal))

Mein Versuch:

let Lagrange(p:_[], v, desiredPos) =
    let Seq_multiply = Seq.fold (*) 1.0
    let distance i j = if (i=j) then 1.0 else (desiredPos-p.[j])/(p.[i]-p.[j])
    let weight i = p |> Seq.mapi (fun j _ -> distance i j) |> Seq_multiply
    v |> Seq.mapi (fun i vi -> (weight i)*vi) |> Seq.sum

Umgestalten durch die innere Schleife eine Funktion macht. Auch können wir den Code einfacher und „verständlich“ durch die Definition einige sinnvolle Funktionen machen.

Auch hebt diese Rewrite einen Fehler in Ihrem ursprünglichen Code (und alle anderen Varianten). Die Abstandsfunktion eigentlich sein sollte:

let distance i j = if (p.[i]=p.[j]) then 1.0 else (desiredPos-p.[j])/(p.[i]-p.[j])

die allgemeinen div-by-Null-Fehler zu vermeiden. Dies führt zu einer allgemeinen und indexless Lösung:

let Lagrange(p, v, desiredPos) =
    let distance pi pj = if (pi=pj) then 1.0 else (desiredPos-pj)/(pi-pj)
    let weight pi vi = p |> Seq.map (distance pi) |> Seq.fold (*) vi
    Seq.map2 weight p v |> Seq.sum
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top