博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
强大的C# Expression在一个函数求导问题中的简单运用
阅读量:4320 次
发布时间:2019-06-06

本文共 2462 字,大约阅读时间需要 8 分钟。

【问题梗概】

求一个函数的一阶导数。

【代码方案】

  1. using System;  
  2. using System.Linq.Expressions;  
  3.   
  4. namespace Derivative  
  5. {  
  6.     class Program  
  7.     {  
  8.         // 求一个节点表达的算式的导函数  
  9.         static Expression GetDerivative(Expression node)  
  10.         {  
  11.             if (node.NodeType == ExpressionType.Add   
  12.                 || node.NodeType == ExpressionType.Subtract)  
  13.             {   // 该节点在做加减法,套用加减法导数公式  
  14.                 BinaryExpression binexp = (BinaryExpression)node;  
  15.                 Expression dleft = GetDerivative(binexp.Left);  
  16.                 Expression dright = GetDerivative(binexp.Right);  
  17.                 BinaryExpression resbinexp;  
  18.   
  19.                 if (node.NodeType == ExpressionType.Add)  
  20.                     resbinexp = Expression.Add(dleft, dright);  
  21.                 else  
  22.                     resbinexp = Expression.Subtract(dleft, dright);  
  23.                 return resbinexp;  
  24.             }  
  25.             else if (node.NodeType == ExpressionType.Multiply)  
  26.             {   // 该节点在做乘法,套用乘法导数公式  
  27.                 BinaryExpression binexp = (BinaryExpression)node;  
  28.                 Expression left = binexp.Left;  
  29.                 Expression right = binexp.Right;  
  30.   
  31.                 Expression dleft = GetDerivative(left);  
  32.                 Expression dright = GetDerivative(right);  
  33.   
  34.                 return Expression.Add(Expression.Multiply(dleft, right),   
  35.                     Expression.Multiply(left, dright));  
  36.             }  
  37.             else if (node.NodeType == ExpressionType.Parameter)  
  38.             {   // 该节点是x本身(叶子节点),故而其导数即常数1  
  39.                 return Expression.Constant(1.0);  
  40.             }  
  41.             else if (node.NodeType == ExpressionType.Constant)  
  42.             {   // 该节点是一个常数(叶子节点),故其导数为零  
  43.                 return Expression.Constant(0.0);  
  44.             }  
  45.   
  46.             throw new NotImplementedException();    // 其余的尚未实现          
  47.         }  
  48.   
  49.         static Func<double, double> GetDerivative(Expression<Func<double, double>> func)  
  50.         {  
  51.             // 从Lambda表达式中获得函数体  
  52.             Expression resBody = GetDerivative(func.Body);  
  53.   
  54.             // 需要续用Lambda表达式的自变量  
  55.             ParameterExpression parX = func.Parameters[0];  
  56.   
  57.             Expression<Func<double, double>> resFunc  
  58.                 = (Expression<Func<double, double>>)Expression.Lambda(resBody, parX);  
  59.   
  60.             // 打印看一下导数解析式,在这个实现中结果是1+1*x+x*1(因为缺乏优化)  
  61.             Console.WriteLine("diff function = {0}", resFunc);  
  62.   
  63.             // 编译成CLR的IL表达的函数  
  64.             return resFunc.Compile();  
  65.         }  
  66.   
  67.         static double GetDerivative(Expression<Func<double, double>> func, double x)  
  68.         {  
  69.             Func<double, double> diff = GetDerivative(func);  
  70.             return diff(x);  
  71.         }  
  72.   
  73.         static void Main(string[] args)  
  74.         {  
  75.             // 举例:求出函数f(x) = x*x+x 在x=32处的导数  
  76.             double y = GetDerivative(x => x * x + x, 32);  
  77.             Console.WriteLine("f'(x) = {0}", y);  
  78.         }  
  79.     }  
  80. }  
【实现大意】
用表达式分解并递归求导(过程是相当容易的,比想象的还容易)。目前只是实现了一个最简单的模型。
【优势】
给出的是解析解,在求导运算方面没有任何数值解的误差,输出运算也是瞬时的,时间复杂度仅和表达式复杂度相关。
【限制】
1. 函数只能以Lambda表达式输入,只能是能求出解析解的表达式
2. 目前只实现了加减法和乘法
【后续扩展】
1. 实现其他运算符(没有太大难度,只是比较繁琐而已)
2. 表达式树优化(也不太难的,根据情况定),最基本的可以从常数乘法开始……
3. 条件运算符的处理(这个会变得极难极复杂,但一定程度上实现分段函数求导),其他特殊情况(对求导还可以,如果考虑求不定积分问题可能会有很多特殊情况和hardcode)
4. 输入端向字符串解析过渡;复杂运算符->逐渐向自定义的数据结构过渡?……

转载于:https://www.cnblogs.com/shihao/archive/2011/10/05/2199358.html

你可能感兴趣的文章
【loj6038】「雅礼集训 2017 Day5」远行 树的直径+并查集+LCT
查看>>
十二种获取Spring的上下文环境ApplicationContext的方法
查看>>
UVA 11346 Probability 概率 (连续概率)
查看>>
linux uniq 命令
查看>>
Openssl rand命令
查看>>
HDU2825 Wireless Password 【AC自动机】【状压DP】
查看>>
BZOJ1015: [JSOI2008]星球大战starwar【并查集】【傻逼题】
查看>>
HUT-XXXX Strange display 容斥定理,线性规划
查看>>
mac修改用户名
查看>>
一道关于员工与部门查询的SQL笔试题
查看>>
Canvas基础
查看>>
[Hive - LanguageManual] Alter Table/Partition/Column
查看>>
可持久化数组
查看>>
去除IDEA报黄色/灰色的重复代码的下划波浪线
查看>>
Linux发送qq、网易邮件服务配置
查看>>
几道面试题
查看>>
【转】使用 WebGL 进行 3D 开发,第 1 部分: WebGL 简介
查看>>
js用正则表达式控制价格输入
查看>>
chromium浏览器开发系列第三篇:chromium源码目录结构
查看>>
java开发操作系统内核:由实模式进入保护模式之32位寻址
查看>>