得到拉格朗日插值的多项式表示

时间:2014-09-23 20:29:41

标签: f#

我如何表示不完整的数学函数?

我需要做(x - constant)然后

之类的事情
(x - constant)*(x - another) => (x^2 - x * constant - x * another + constant * another)

等等。

我正在尝试制作拉格朗日插值程序(找到某些点的函数) 所以我需要从一组已知的值中创建一个我可以看到的函数(print或者其他东西)。 抱歉,如果混淆。

2 个答案:

答案 0 :(得分:4)

如果您想按照here

所述实施拉格朗日插值

获取插值的函数:

然后这是直接翻译成F#:

let LagrangeInterpol (points : (Double*Double)[]) x =
    let indizes = [0..points.Length-1]
    let p j = 
        indizes
        |> List.map (fun k -> 
            if k <> j 
            then (x - fst points.[k]) 
                  / (fst points.[j] - fst points.[k]) 
            else 1.0)
        |> List.fold (*) 1.0 
    indizes |> List.sumBy (fun j -> p j * snd points.[j])

实施例

这是一个简单的测试会话:

> let points = [|0.0,0.0; 1.0,2.0; 2.0,3.0|];;
val points : (float * float) [] = [|(0.0, 0.0); (1.0, 2.0); (2.0, 3.0)|]

> let f = LagrangeInterpol points;;
val f : (Double -> float)

> f 0.0;;
val it : float = 0.0

> f 1.0;;
val it : float = 2.0

> f 2.0;;
val it : float = 3.0

所以我希望我没有犯任何重大错误。

请注意,我没有在这里做任何性能优化的efford - 这应该足以绘制图表或在两者之间获得一些值。

得到多项式的表示

这有点棘手 - 你可以试着想出系数的组合公式,或者(比如我这里)是数学上懒惰的,只需用足够的算子实现Polynom-Type:

type Polynom = 
    Poly of float list with
    override p.ToString () =
        match p with
        | Poly coefs -> 
            System.String.Join (" + ", coefs |> List.mapi (fun i c -> sprintf "%AX^%d" c i))
    static member Const c = Poly [c]
    static member Zero = Polynom.Const 0.0
    static member One = Polynom.Const 1.0
    static member X = Poly [0.0; 1.0]
    static member (+) (Poly cs1, Poly cs2) =
        let m = max (List.length cs1) (List.length cs2)
        List.zip (ofLen m cs1) (ofLen m cs2)
        |> List.map (fun (a,b) -> a+b)
        |> Poly
    static member (-) (Poly cs1, Poly cs2) =
        let m = max (List.length cs1) (List.length cs2)
        List.zip (ofLen m cs1) (ofLen m cs2)
        |> List.map (fun (a,b) -> a-b)
        |> Poly
    static member (*) (f : float, Poly cs2) : Polynom =
        cs2
        |> List.map (fun c -> f * c)
        |> Poly
    static member private shift n (Poly cs) =
        List.replicate n 0.0 @ cs |> Poly
    static member (*) (Poly cs1, p2 : Polynom) : Polynom =
        cs1
        |> List.mapi (fun i c -> Polynom.shift i (c * p2))
        |> List.sum
    static member (/) (Poly cs1, f : float) : Polynom =
        cs1
        |> List.map (fun c -> c / f)
        |> Poly

这里我只使用float列表来表示多项式的系数(所以X^2 + 2X + 3Poly [3.0; 2.0; 1.0]注意i系数是{ X^i

有了这个,我们可以使用几乎与以前相同的功能:

let getPolynom (points : (float * float)[]) =
    let indizes = [0..points.Length-1]
    let p j = 
        indizes
        |> List.map (fun k -> 
            if k <> j 
            then (Polynom.X - Polynom.Const (fst points.[k]))
                  / (fst points.[j] - fst points.[k]) 
            else Polynom.One)
        |> List.fold (*) Polynom.One
    indizes |> List.sumBy (fun j -> Polynom.Const (snd points.[j]) * p j)      

正如您所看到的,我使用了相同的函数,并且只用x替换了参数Polynom.X并将常量包装为approbiatley。

实施例

以下是两个例子(将它们与它们应该正确的Wiki-Page进行比较):

> LagrangeInterpolation.getPolynom 
    [|(1.0, 1.0); (2.0, 4.0); (3.0, 9.0)|] |> string;;
val it : string = "0.0X^0 + 0.0X^1 + 1.0X^2"

> LagrangeInterpolation.getPolynom 
     [| 1.0,1.0; 2.0,8.0; 3.0,27.0 |] |> string;;
val it : string = "6.0X^0 + -11.0X^1 + 6.0X^2"

包含帮助者的完整代码

模块内部的完整代码是:

module LagrangeInterpolation =

    let private ofLen n cs =
        let l = List.length cs
        if l < n
            then cs @ List.replicate (n-l) 0.0
            else cs

    type Polynom = 
        Poly of float list with
        override p.ToString () =
            match p with
            | Poly coefs -> 
                System.String.Join (" + ", coefs |> List.mapi (fun i c -> sprintf "%AX^%d" c i))
        static member Const c = Poly [c]
        static member Zero = Polynom.Const 0.0
        static member One = Polynom.Const 1.0
        static member X = Poly [0.0; 1.0]
        static member (+) (Poly cs1, Poly cs2) =
            let m = max (List.length cs1) (List.length cs2)
            List.zip (ofLen m cs1) (ofLen m cs2)
            |> List.map (fun (a,b) -> a+b)
            |> Poly
        static member (-) (Poly cs1, Poly cs2) =
            let m = max (List.length cs1) (List.length cs2)
            List.zip (ofLen m cs1) (ofLen m cs2)
            |> List.map (fun (a,b) -> a-b)
            |> Poly
        static member (*) (f : float, Poly cs2) : Polynom =
            cs2
            |> List.map (fun c -> f * c)
            |> Poly
        static member private shift n (Poly cs) =
            List.replicate n 0.0 @ cs |> Poly
        static member (*) (Poly cs1, p2 : Polynom) : Polynom =
            cs1
            |> List.mapi (fun i c -> Polynom.shift i (c * p2))
            |> List.sum
        static member (/) (Poly cs1, f : float) : Polynom =
            cs1
            |> List.map (fun c -> c / f)
            |> Poly

    let getPolynom (points : (float * float)[]) =
        let indizes = [0..points.Length-1]
        let p j = 
            indizes
            |> List.map (fun k -> 
                if k <> j 
                then (Polynom.X - Polynom.Const (fst points.[k]))
                      / (fst points.[j] - fst points.[k]) 
                else Polynom.One)
            |> List.fold (*) Polynom.One
        indizes |> List.sumBy (fun j -> Polynom.Const (snd points.[j]) * p j)      

备注

为了获得更好的输出,您应该添加一些简化(例如Poly [1.0;0.0] -> Poly [1.0])并改进ToString方法,但我确定您可以处理;)

答案 1 :(得分:1)

如果你的意思是 partial 的函数,即它的某些输入未定义,那么通常有两种方法可以解决这个问题。一种选择是使用option<'T>类型并在Some中包装正确的结果,或者在未定义值时返回None。例如:

let safeDivide a b =
    if b = 0 then None else Some(a / b)

调用者不得不对结果进行模式匹配(或者使用类似Maybe computation builder之类的东西),这会使函数调用更加困难,但您可以完全控制错误的处理方式。

另一种选择是抛出异常。对于整数除法,这会自动发生,但您可以这样写:

let safeDivide a b =
    if b = 0 then invalidArg "b" "Division by zero!"
    a / b

这更容易编写,但您需要了解行为并正确处理异常。