天天看点

F#表达式求导

      Matlab可以对数学公式进行求导操作,这里用F#实现一个简单的表达式求导推理。首先还是定义一个自定义的Expr数据类型:

type Expr = 
  | CstF of float
  | Var of string
  | Add of Expr * Expr  // +
  | Sub of Expr * Expr // -
  | Mul of Expr * Expr // *
  | Div of Expr * Expr // / 
  | Pow of Expr * Expr // ^ 
  | Sin of Expr  
  | Cos of Expr  
  | Neg of Expr       

   定义一个递归的求导函数diff,由于求导的规则比较多,这里就简单的实现几个场景的求导公式:

let rec diff e  =
    match e with
    | CstF f                -> CstF 0.0
    | Var x                 -> CstF 1.0 
    | Add(CstF a, Var x)   ->  CstF 1.0 
    | Add(e1, e2)  -> Add(diff e1, diff e2)
    | Sub(e1, e2)  -> Sub(diff e1, diff e2)
    | Mul(CstF a, Var x) ->  CstF a 
    | Mul(e1, e2) ->  Mul(diff e1, diff e2)
    | Pow(Var x,CstF a) -> Mul(CstF a,Pow(Var x,CstF (a - 1.)))
    | Pow(e1,e2)  -> Mul(e2,Pow(e1, Sub(e2,CstF 1.)))
    | Sin(e1)  -> Mul(Cos(e1),diff e1)
    | Cos(e1)  -> Mul(Neg(Sin(e1)),diff e1)
    | Neg(e1)  -> Neg(diff e1)
    | e          -> e ;;      

   为了更到的输出DSL到控制台,定义一个打印表达式的函数:

let rec printExpr2 e =
    match e with
    | CstF f            -> string f
    | Var x             ->  x 
    | Add(e1 , e2) ->  "(" + (printExpr2 e1) + "+" + (printExpr2 e2) + ")"
    | Sub(e1 , e2) ->  "(" + (printExpr2 e1) + "-" + (printExpr2 e2) + ")"
    | Mul(e1 , e2) ->  "(" + (printExpr2 e1) + "*" + (printExpr2 e2) + ")"
    | Div(e1 , e2) ->  "(" + (printExpr2 e1) + "/" + (printExpr2 e2) + ")"
    | Pow(e1 , e2) ->  "(" + (printExpr2 e1) + "^" + (printExpr2 e2) + ")"
    | Sin(e1) ->  "sin(" + (printExpr2 e1) + ")"
    | Cos(e1) ->  "cos(" + (printExpr2 e1) + ")"
    | Neg(e1) ->  "-(" + (printExpr2 e1) + ")"
    | _          -> failwith "unknown operation";;      

    严格来讲,每个操作的前后用()进行分组应该更加严谨,但是过多的()嵌套会让数学表达式不容易理解。但,关于括号的嵌套化简问题,还需要一定的工作才能工作,这里先不进行处理。下面定义一个求导示例:

let e1 = Sin(Sub(Pow(Var "x", CstF 3.0), Var "x")) ;; 
let e2 = diff e1;;
printExpr2 e2;;      

  执行如上代码,则输出如下图所示:

F#表达式求导

 这与网上的求导工具(

https://zh.numberempire.com/derivativecalculator.php

)求出的一致:

F#表达式求导

继续阅读