zoukankan      html  css  js  c++  java
  • 强大的C# Expression在一个函数求导问题中的简单运用

    【问题梗概】

    求一个函数的一阶导数。

    【代码方案】

    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. 输入端向字符串解析过渡;复杂运算符->逐渐向自定义的数据结构过渡?……
  • 相关阅读:
    5,MongoDB 之 "$" 的奇妙用法
    4,MongoDB 之 $关键字 及 $修改器 $set $inc $push $pull $pop MongoDB
    3,MongoDB之数据类型
    2,MongoDB之增删改查及pymongo的使用
    1,MongoDB简介和安装
    19,Ubuntu安装之python开发
    18,Shell编程实战
    17,saltstack高效运维
    16.2,docker网络
    如何使用 window api 转换字符集?
  • 原文地址:https://www.cnblogs.com/shihao/p/2199358.html
Copyright © 2011-2022 走看看