2009-10-21 11 views
5

बस एफ # के साथ के बारे में खिलवाड़ और मैं इस सी # संस्करण (एक सी ++ विकी प्रविष्टि से नकल) के आधार पर एक बुनियादी Lagrange अंतर्वेशन समारोह बनाने के लिए कोशिश कर रहा था:नए सिरे से लिखना सी # कोड

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; 
    } 

सबसे अच्छा मैं आ सकता है एफ # और कार्यात्मक प्रोग्रामिंग का अपने सीमित ज्ञान का उपयोग कर के साथ था:

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) 

किसी को एक बेहतर/tidier एफ # ही सी # कोड के आधार पर संस्करण प्रदान कर सकते हैं?

+3

मुझे लगता है कि यह जब जरूरी कोड पढ़ सकते हैं और बनाए रखने के लिए आसान कार्यात्मक से है का एक अच्छा उदाहरण है। – gradbot

उत्तर

4

अनुक्रमों पर फोल्डिंग एक संचयक के साथ लूप को प्रतिस्थापित करने का एक आम तरीका है।

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 
+0

के लिए मूल सी # कोड के समान उत्तर देता है ब्रायन संपादित करने के लिए धन्यवाद। – gradbot

1
  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) 

निजी तौर पर मुझे लगता है कि सरल है, तो/elif/बाकी निर्माणों

match i with 
|i when i=... 
2

के रूप में यहां काफी बेहतर लग रही है इस तरह के ओवरहेड्स बिना यहाँ एक गैर पुनरावर्ती समाधान है। यह थोड़ा अजीब है क्योंकि एल्गोरिथ्म सूचकांक की आवश्यकता है, लेकिन उम्मीद है कि यह दिखाता है कि एफ # के कार्यों की रचना की जा सकती है:

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) 
3

मैं इस काम करता है ठीक के रूप में अनिवार्य कोड लगता है:

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 

लेकिन आप कार्यात्मक चाहते हैं, कुछ परतों (MAPI के साथ जब से तुम अक्सर साथ सूचकांक करने की जरूरत) अच्छी तरह से काम:

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 

मैं गणित यहाँ पता नहीं है, तो मैं करने के लिए (उम्मीद) ई परीक्षण करने के लिए कुछ यादृच्छिक उपयोग में लिए गए nsure मैंने कुछ भी खराब नहीं किया:

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 
+0

आप अपने फोल्ड accumulator सूचकांक युक्त एक tuple होने के द्वारा मैपी के साथ भी दूर कर सकते हैं। v |> गुना (मजेदार (retVal, i) posi -> newRetValuefunction, i + 1) (0.0, 0) – gradbot

+0

यह मेरे डेटा –

4

बात यह है कि अपने कार्यात्मक समाधान बदसूरत i'th तत्व है, जो सूचकांक का मतलब लंघन है बनाता है। इसे एक पुन: प्रयोज्य कार्य में खींचें ताकि सभी बदसूरत इंडेक्स हैंडलिंग अलग हो। मैं अपने राउंडरोबिन को बुलाता हूं।

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) 
} 

यदि आप एक कुशल संस्करण बनाना चाहते हैं तो यह बहुत अधिक हो सकता है।

मुझे सेक मॉड्यूल में product नहीं मिला, इसलिए मैंने अपना खुद लिखा।

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

अब कोड का उत्पादन काफी सरल है:

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 सुनिश्चित करता है कि स्थिति [i] भीतरी पाश में स्थिति के बाकी के साथ शामिल नहीं है। val सरणी को शामिल करने के लिए, मैंने इसे राउंड-रॉबिन pos सरणी के साथ ज़िपित किया।

यहां सबक यह है कि इंडेक्सिंग एक कार्यात्मक शैली में बहुत बदसूरत है। इसके अलावा मुझे एक अच्छी चाल मिली: |> Seq.append <| आपको दृश्यों को जोड़ने के लिए इंफिक्स वाक्यविन्यास देता है। हालांकि ^ के रूप में काफी अच्छा नहीं है।

1

यदि आप बस इसके बारे में गड़बड़ कर रहे हैं तो यहां ब्रायन के समान संस्करण है जो फ़ंक्शन करी और ट्यूपल पाइप ऑपरेटर का उपयोग करता है।

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)) 
0

मेरे प्रयास: भीतरी पाश एक समारोह बनाकर

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 

Refactor। इसके अलावा हम कोड को कुछ सार्थक कार्यों को परिभाषित करके अधिक सरल और "समझने योग्य" बना सकते हैं।

इसके अलावा, यह पुनः लिखना आपके मूल कोड (और अन्य सभी प्रकारों) में एक बग को हाइलाइट करता है। दूरी समारोह वास्तव में होना चाहिए:

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

सामान्य div-by-zero त्रुटि से बचने के लिए। यह एक सामान्य और indexless समाधान की ओर जाता है:

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 
संबंधित मुद्दे