求导的符号运算

求导的符号运算

  这是最近做的一个python的小项目,内容是实现求导的符号运算,虽然我最终的实现化简能力能力比较弱,但是还是一个很有意思的项目。
  


语法树的建立

语法树的结构

  语法树是我这个项目里面所有表达式的存在形式。
  
  树的一个结点要么是字符串(常数或者变量),要么是内部结点,内部结点代表一棵子树,对应存储的结构体有两个字段:根存储的运算和子结点的列表,列表元素个数是操作符的元数,列表元素是子结点。
  
  例如前缀表达式(+ (* x 2) 1)表示2x+1,那么对应的树根结点是’+’,对应的子结点列表是[tree(2x), 1],其中tree(2x)又是一棵子语法树,根为’*’, 对应子结点的列表是[2, x]。即语法树是递归定义的,最终叶子结点是常量或者变量。

前缀表达式转化为语法树

  
  我的项目中约定输入前缀表达式,即类似lisp的表达式,所以转化非常方便。
  
  对于前缀表达式(op arg0, arg1, …, argn),先读入op为根结点操作符,然后读入参数列表即可建立语法树,注意读入参数列表过程中可能读到表达式,需要递归建树。


语法树的运算

求值

  
  利用前缀表达式的算法递归求值即可。遇到变量,代入具体数值即可。
  
  设calculate(tree,x)计算tree在变量取值x时候的值。

求函数

  
  因为要求一个函数而不是具体数值,所以需要返回一个函数或者lambda表达式,可以实现func(tree) 为lambda x: calculate(tree, x)。

求导

  这里是算法的核心。

  对于二元运算,算法为递归求导,规则如下:
  1.叶子结点:x导数为1,常数导数为0。
  2.非叶子结点,左右子树为l,r。那么结点的运算来递归求导:
   (l+r)=l+r
   (lr)=lr
   (lr)=lr+lr
   (l/r)=(lrlr)/(r2)
   (lr)=(rlr1)l+ln(l)lrr
  手动实现这五条规则即可。

  对于一元函数,有统一的计算方法,即链式法则:
   h(g(x))=h(g(x))g(x)
  所以需要保存一个函数表,保存下每一个函数的导函数,利用链式法则递归计算即可。注意我们这儿并不保存导函数的函数形式,而是保存如何建立导函数语法树的lambda表达式。、
  例如,ln(x)的导函数就是lambda x: ExpTree(‘\’, [1, x])。


语法树化简

  
  因为上述的求导规则经常会产生0或者1项,例如:
   (2x)=(2)x+2(x)=0x+21
  
  所以需要化简,规则为:
  1. 0+M=M+0=M
  2. M0=M
  3. 1M=M1=M
  4. 0M=M0=0
  5. M/1=M
  6. 0/M=0
  7. M1=M
  8. 0M=0
  手动实现规则即可。
  
  要注意的是,需要先化简子树来递归化简。另外,如果左右子树都是常数,可以直接计算。
  


界面和交互

  界面需要用户输入前缀表达式和运算指令,输出对应的函数或者函数值,另外输出对应函数的时候,为了直观,除了函数的中缀表达式,我还输出了函数图像。
  
  交互上我没有花和大力气,所以格式相对比较死板,不过提供了足够的帮助信息,应该用起来很方便。
  


还需要改进的地方

  但是,这毕竟只是一个耗时一天不到的小项目,要改进的地方还有很多:
  
  1.这个项目不能运用运算律和函数性质化简,所以以下这些化简,暂时无法完成:
  x-x=0,2x+3x=5x,sinx * sinx + cosx * cosx=1,ln(exp(x))=x。
  
  2.这个项目暂时不支持自己指定中间函数,这个涉及到词法语法分析等等,没有实现。

  3.对于重复表达式没有共享而是一一计算,没有优化。

  但是毕竟这是一个轻量级项目,所以不准备加入这些特性了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值