문제

F#을 엉망으로 만들면이 C# 버전 (C ++ Wiki 항목에서 복사)을 기반으로 기본 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;
    }

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)

누군가가 같은 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

다른 팁

기능적 솔루션을 못 생겼다는 부분은 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 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은 POS [i]가 내부 루프의 나머지 PO에 포함되지 않도록합니다. 포함 val 배열, 나는 둥근 로브 린으로 그것을 zed습니다 pos 정렬.

여기서 교훈은 인덱싱이 기능적 스타일로 매우 추악하다는 것입니다. 또한 멋진 트릭을 발견했습니다. |> Seq.append <| 추가 시퀀스에 대한 Infix Syntax를 제공합니다. 그다지 좋지 않습니다 ^ 그렇지만.

나는 이것이 필수 코드로 잘 작동한다고 생각합니다.

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

다음은 비수체적인 솔루션입니다. 알고리즘에 인덱스가 필요하기 때문에 약간 펑키하지만 F#의 함수가 어떻게 구성 될 수 있는지 보여줍니다.

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)

개인적으로 나는 간단한 if/elif/constructs가 다음과 같은 오버 헤드없이 훨씬 더 잘 보인다고 생각합니다.

match i with   
|i when i=...

만약 당신이 엉망이된다면, 여기에는 기능 카레와 튜플 파이프 연산자를 사용하는 Brian과 유사한 버전이 있습니다.

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-by-zero 오류를 피하기 위해. 이것은 일반적이고 색인이없는 솔루션으로 이어집니다.

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