سؤال

مجرد العبث مع F# وكنت أحاول إنشاء الأساسية Lagrange الاستيفاء وظيفة على أساس هذا C# نسخة (نسخ من C++ ويكي الدخول):

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

أفضل ما يمكن أن تأتي مع استخدام معرفتي محدودة F# وظيفية البرمجة:

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)

يمكن للشخص تقديم أفضل/أرتب F# الإصدار على أساس نفس C# رمز ؟

هل كانت مفيدة؟

المحلول

قابلة للطي على تسلسل هو وسيلة شائعة استبدال الحلقات مع المجمع.

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

نصائح أخرى

الجزء الذي يجعل حل عملي القبيح هو تخطي أنا أخذ عنصر ، مما يعني أن المؤشرات.اسحب إلى الاستخدام وظيفة كل قبيح مؤشر التعامل مع معزولة.أدعو لي 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)
}

يمكن أن يكون الكثير أقبح إذا كنت تريد أن تنتج بكفاءة الإصدار ، على الرغم من.

لم أجد product في Seq وحدة ، لذلك كتبت بلدي.

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 يضمن نقاط البيع[أنا] لا يتم تضمين مع بقية نقاط البيع في الحلقة الداخلية.تشمل val مجموعة, أنا مضغوط مع المستديرة robinned pos الصفيف.

الدرس هنا هو أن الفهرسة هو قبيح جدا في نمط وظيفي.كما اكتشفت خدعة رائعة: |> Seq.append <| يعطيك أقحم الجملة من أجل إلحاق متواليات.ليس لطيفا كما ^ على الرغم من.

أعتقد أن هذا يعمل بشكل جيد كما حتمية كود:

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

أنا لا أعرف الرياضيات هنا ، لذا تستخدم بعض القيم العشوائية لاختبار (أمل) ضمان لقد أخطئت في شيء:

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

وهنا غير حل العودية.انها قليلا غير تقليدي لأن خوارزمية يتطلب المؤشرات ، ولكن نأمل أن يظهر كيف و#'s وظائف يمكن أن تتكون:

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)

شخصيا أعتقد أنه بسيط إذا/أليف/آخر يبني انظر هنا أفضل بكثير من دون هذه النفقات العامة كما

match i with   
|i when i=...

إذا كنت مجرد العبث ثم ها هي نسخة مشابهة براين يستخدم وظيفة التمشيط و tuple الأنابيب المشغل.

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

محاولتي:

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

ريفاكتور بجعل الحلقة الداخلية وظيفة.ونحن أيضا يمكن أن تجعل القانون أكثر وضوحا و "المفهوم" من خلال تحديد بعض معنى وظائف.

أيضا, كتابة هذا يسلط الضوء على خطأ في رمز الأصلي (جميع المتغيرات الأخرى).المسافة وظيفة يجب أن يكون في الواقع:

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

لتجنب العامة div عن طريق الخطأ صفر.وهذا يؤدي إلى عامة 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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top