与C#的符号集成

目录

介绍

背景

集成算法

实现

符号集成示例

扩展集成能力

结论


符号集成功能可用于许多科学、工程和教育软件。功能的实现不是一件容易的事。在本文中,我们将描述一种简单的集成算法及其在符号计算库中的实现方式。积分算法包括所有积分规则和基本积分公式。该实现还允许使用新的高级集成方法来补充算法。我们将显示符号积分的几个示例,并将提供有关如何扩展库功能的指南。该库可以轻松地在任何.NET应用程序中构建。

介绍

符号计算(计算机代数)是计算机科学研究领域之一。它处理数学公式(表达式),并提供用于以自然形式操纵表达式的计算机算法,而无需将表达式简化为数值。主要是在称为计算机代数系统(CAS)的特殊软件中使用符号计算。现代计算机代数系统包括各种符号算法,从简化复杂的数学公式到处理描述各种物理场的偏微分方程。CAS软件通常具有图形用户界面(GUI),以自然数学形式显示结果。可以将此类软件用作计算机助手,以帮助他用数学公式进行例行运算。

但是,除了CAS之外,还有很多软件需要符号算法作为其功能之一。例如,现代的计算机辅助工程(CAE)系统包括后处理模块。该模块允许对CAE系统获得的计算结果进行高级处理。该功能的常见实现之一是提供用户定义的公式进行处理。因此,该软件应包括用于符号操作的工具。在许多工程、科学和教育应用中也需要符号数学模块。这种应用程序的主要功能不是提供完整的符号数学,而是应将一些符号算法作为软件的一部分。

最有趣的两个符号计算是微分和积分。这两个操作有很多共同点。至少应注意,集成的一种强大方法是零件集成。该方法包括微分作为积分算法的阶段之一。另一方面,符号集成比符号区分要困难得多。

本文介绍了一种可能的简单符号集成方法。这种方法在C#符号库ANALYTICS中实现(可以从此处下载)。该实现的优点之一是可以在不修改核心算法的情况下将新的集成算法引入库中。该库可以轻松地嵌入C#应用程序中,并且可以为开发软件提供许多符号功能。

背景

让我们首先从算法的角度说明差异化与集成化之间的主要区别。这将帮助我们更好地理解实现符号集成的方法。数学表达式(公式)由常数(数字、标准常数,例如PiEuler值),变量(通常给定的名称xyz等),代数运算(+-*/等)和函数(sincoslnexp等)组成。关于差异,我们可以说的是,有一种简单的递归算法可以获取任何表达式的派生词(如果存在)。为什么会这样呢?这是因为我们对表达式所包含的每种成分都有这样的算法。

考虑代数运算。我们对所有这些都有区分规则。例如,我们知道和的导数满足以下规则:

我们知道产品规则:

对于所有类型的代数运算,包括幂和根,都有微分规则。

然后,考虑功能。我们知道大多数基本先验和特殊功能的导数。对于复合函数(函数的功能),我们具有微分的链式规则:

因此,有了所有表达式元素的公式和导数规则,我们可以提供一种区分任何复杂表达式的算法。

现在,让我们继续进行集成。我们对所有类型的表达式都有集成规则吗?答案是NO。让我们考虑代数运算。实际上,我们仅对两种情况具有积分规则——乘以常数和和的积分:

 

对于其他代数运算(例如乘法、幂等)情况,我们没有简单的公式。考虑到函数,我们遇到了相同的情况。我们知道大多数基本函数的积分公式,但是对于复合函数没有积分规则。

总结起来,与符号微分相反,我们不能应用一些简单的符号积分递归算法。取而代之的是,我们应该使用特殊的集成方法,例如按部件集成,按替换集成等。集成规则和集成方法之间的区别非常细微。通常,作为基于某些公式的方法。但是该规则提供了一个简单的公式,仅取决于表达式的类型。该公式可以应用于任何类型的表达式。另一方面,集成方法对表达式所包含的元素很敏感。积分方法只是指向可导致表达式最终积分的一种方法。但是我们不知道选择哪种方式。

集成算法

我们将在这里实现的是一种符号集成算法。该算法必须包括以下功能:

  • 整合规则的实现
  • 基本积分公式的实现
  • 用于实现功能积分的接口
  • 实现集成方法的接口

该算法的第一点已在上一节中介绍。第二点假设我们还必须在积分引擎中包括诸如1/x’, ‘x^n的积分等公式。我们称它们为积分公式,而不是积分规则,因为它们只是为具体表达式定义积分的公式。例如,相对于“x”“1/x”的积分为ln(x)。这是一个公式,而不是规则,因为我们不能将其应用于通用表达式1/u(x)。最后两点指出,算法的实现必须提供这样的接口,即允许在不修改积分核心算法的情况下为函数积分和新的高级积分方法添加新公式。

现在该看一下如何在ANALYTICS C#库中实现此算法了。ANALYTICS是用于评估数学公式的通用库。该库的主要组件之一是'BaseExpression'类,该类封装了用于处理数学表达式的基本功能。这是表达式的类层次结构:

让我们解释一些集成主题最感兴趣的类。层次结构中的简单表达式是文字(常量)和变量。我们可以很容易地实现这些表达式的积分公式:常数“c”“x”的积分为“c*x”“x”“x”的积分为x^2/2

所有代数运算都分为一元运算符和二进制表达式。一元运算符停留在减号、阶乘号和其他一些运算符上。我们可以实现负运算符的积分规则:只需将常数乘数'-1'移到积分上即可。让我们注意到阶乘'x!'的积分。未定义,因此我们必须在算法中包括这种情况。

二进制运算表达式是具有两个操作数的代数运算的序列。和表达式的一个示例是x+sin(y)-1。对于这种类型的表达式,必须执行求和规则,如上一节中所述。乘积表达式是一个乘法序列,例如'A*x/2'。我们必须实现乘积表达式的积分规则,即,必须将不依赖积分变量的表达式的常量部分移到积分上。

根据集成变量,某些二进制表达式无法集成。例如,以下逻辑表达式“xy”不存在“x”的整数。其他一些二进制表达式不假定任何集成规则,但是它们必须为表达式的简单情况提供一个集成公式。例如,幂表达式应实现“x^n”“a^x”的积分公式。

最后,函数表达式实现了数学函数的概念,例如正弦、余弦、自然对数等。在积分算法中,我们必须提供函数积分的接口。也就是说,我们必须能够为任何先验函数添加积分公式,而无需更改核心积分算法。对于所有其他复杂的表达式,我们必须提供可以添加到集成引擎中的高级集成方法的类似接口。

该算法的描述听起来有些复杂,的确如此。符号集成不是一件容易的事。让我们考虑一下C#代码,它将阐明算法。

实现

所描述的集成算法以'BaseExpression'类的'Integrate'方法实现。这是该方法的部分代码,为清楚起见,省略了一些次要细节:

public virtual BaseExpression Integrate(string vName)
{
    BaseExpression result = null;

    if (!this.DependsOn(vName))
    {
        result = this * (new VariableExpression(vName));
        return result;
    }

    result = this.SelfIntegral(vName);

    if (result == null)
    {
        foreach (Integrator ii in Context.Integrals.ExpressionIntegrators)
        {
            result = ii.Integral(this, vName);
            if (result != null)            
            {
                return result;
            }
        }
    }

    return result;
}

函数vName的参数保留为积分变量的名称。由于无法对所有表达式求值不确定积分,因此我们使用这样的约定:当算法无法计算积分时,结果是null。然后,我们检查表达式是否依赖于变量,如果不是,则将表达式乘以变量,最后返回结果(它实现了常数积分的公式)。

如果表达式不是常量(相对于积分变量),则我们首先调用'SelfIntegral'方法。此方法实现了积分算法的前三点,如上一节所述:积分规则;一些简单表达式的积分公式;函数的积分公式。调用此方法带来了递归,因为积分规则和积分公式实现可以调用'Integrate'方法。我们将很快回到这一点,并在一些简单的情况下考虑该方法。

Integrate方法的最后一部分实现了算法的最后一点:它遍历所有注册的积分器并调用其Integral方法。如果其中一个积分器能够计算表达式的不定积分,我们将其作为最终结果返回。Integrator是用于实现高级集成技术的特殊类。'Integrator'类具有三个后代:'TemplateIntegrator'——实现表达式的集成公式,与某些模板匹配;'SimplifyIntegrator'——首先简化表达式,然后为新表达式调用'Integrate'方法;和'AlgorithmIntegrator'——实现通用的集成方法,例如部件集成等。

让我们回到'SelfIntegral'方法,并考虑几种表达式类型的实现。这是'UnaryOperatorExpression'类的方法的C#代码(同样,为了简化起见,代码的某些部分进行了简化):

protected override BaseExpression SelfIntegral(string vName)
{
    BaseExpression result = null;
    OperatorType t = Utilities.GetOperatorType(Sign, OperatorArity.Unary);

    switch (t)
    {
        case OperatorType.Minus:
        {
            BaseExpression opndi = operand.Integrate(vName);
            if (opndi != null)
            {
                result = -opndi;
            }
            break;
        }

        case OperatorType.Factorial:
        {
            throw new ExpressionTypeIntegralException(Sign, Reconstruct());
        }
        
        // ... other unary operators
    }

    return result;
}

可以在代码中看到这种方法如何实现减号运算符的积分规则:首先,它尝试积分表达式的操作数。如果找到了积分(不是null),则取反以获得最终结果(ANALYTICS库重载了代数C#运算符,例如+-*/,以简化对代码中表达式对象的操作)。例如,如果原始表达式为'-x',则操作数为'x'。该操作数的整数为'1/2*x^2',最终结果为'-1/2*x^2'。另一方面,在尝试对阶乘表达式(例如“x!”)进行积分时,会出现异常,因为在这种情况下无法对积分求值。

现在让我们看一下'SumExpression'类的方法代码:

protected override BaseExpression SelfIntegral(string vName)
{
    List<BaseExpression> integrals = new List<BaseExpression>();
    foreach (BaseExpression operand in operands)
    {
        BaseExpression i = operand.Integrate(vName);
        if (i == null)
        {
            return null;
        }

        integrals.Add(i);
    }

    BaseExpression result = new SumExpression("", operators, integrals);
    return result;
}

该代码实现了积分求和规则。它首先找到所有操作数的积分,如果积分计算成功,则返回具有相同运算符的新sum表达式作为最终结果。例如,让我们有以下求和表达式x+1-1/x+A。这里的操作数是“x”“1”“1/x”“A”,运算符是“+”“-”“+”。因此,我们将获得以下操作数的积分:'1/2*x^2’''x''ln(x)''A*x'。然后,我们将使用这三个运算符创建一个新的sum表达式,并得到最终结果为'1/2*x^2+x-ln(x)+A*x'

类似地,如果可能的话,所有其他运算符表达式都将实现积分规则和公式。我们希望实现集成方法的想法很明确,并且在此不提供所有表达式类的代码。剩下的一件事就是如何为函数引入积分公式,以及如何引入高级积分算法。稍后我们将对其进行解释,现在我们将展示一些符号集成的示例。

符号集成示例

ANALYTICS库中符号集成的基本功能是通过'Translator'类的简单接口实现的。这是该接口的常用C#代码模板:

string f = inputTextBox.Text;
try
{
    if (translator.CheckSyntax(f))
    {
        string F = translator.Integral(f, "x");

        // show the result integral F
    }
}
catch (Exception ex)
{
    // process the exception
}

这里的f'是整数的表达式;translatorTranslator类的实例,而F是求值的不确定整数表达式。

我们可以将哪些类型的表达式与已实现的算法集成?该算法支持所有积分规则和几个代数表达式的积分公式。它不支持功能积分和高级积分方法。

首先,让我们评估简单多项式'A*x^2+B*x+C'的积分。这是积分结果:

如预期的那样,该算法成功评估了积分。第一步,它使用求和积分法则;然后,它对所有总和操作数应用了另一个积分规则——在积分上移动一个常数;最后,部署了幂积分公式('x^n'的积分为'x^(n+1)/(n+1)')。

现在,让我们尝试计算更复杂的有理表达式'A/x^3+B/(k*x+b)^2-1/x'的积分。我们得到以下结果:

从生成的公式中我们可以看到,算法中实现了一些高级积分规则。首先,该算法能够将有理表达式转换为幂(例如,将A/x^3转换为A/x^-3),然后应用适当的积分公式。其次,它能够处理参数的线性表达式,即k*x+b,并再次使用适当的公式。

让我们展示该算法如何处理包含有理能力的表达式,例如A*x^(1/2)+B*x^(-3/4)

请注意,该算法如何将幂保持在有理形式以更方便地表示。

受支持的表达式的另一种类型是指数。例如,对表达式'a^x/2+B*e^-x-C/a^(k*x+b)'进行积分,我们得到以下输出:

可以看到,这里的算法还支持变量的线性表达式,即指数k*x+b。它还可以识别标准命名常量,例如“e”“Pi”

我们可以整合一些更复杂的表达式吗?那这个'x*a^x'呢?该表达式由两个元素“x”“a^x”组成,这些元素中的任何一个都可以通过算法轻松处理。不幸的是,该表达式不能与仅实现积分规则和基本积分公式的算法集成。这是因为对于常规产品表达没有集成规则。我们需要为特殊情况的产品实现高级集成方法。

扩展集成能力

我们看到,仅使用积分规则和基本积分公式的核心积分算法能够处理相当有限的数学表达式。但是如上所述,该算法包括允许扩展集成功能的部分:介绍函数集成公式和高级集成方法。

引入函数积分公式的方法非常简单:我们需要实现特殊函数积分器的后代。当类实现时,核心集成算法会自动找到类(具有C#反射机制)并在适当的情况下使用它。

例如,考虑两个三角函数(正弦和余弦)的积分公式的实现:

public sealed class SineIntegral : SimpleFunctionalIntegral
{
    public override BaseExpression BaseIntegral(BaseExpression arg)
    {
        BaseExpression result = FunctionExpression.CreateSimple(Names.Cosine, arg);
        result = UnaryOperatorExpression.Negate(result);
        return result;
    }

    public override string GetFunctionName()
    {
        return Names.Sine;
    }
}

public sealed class CosineIntegral : SimpleFunctionalIntegral
{
    public override BaseExpression BaseIntegral(BaseExpression arg)
    {
        BaseExpression result = FunctionExpression.CreateSimple(Names.Sine, arg);
        return result;
    }

    public override string GetFunctionName()
    {
        return Names.Cosine;
    }
}

后代类重写基本功能积分类的两种方法。GetFunctionName方法返回要集成的函数的名称。BaseIntegral方法使表达式成为函数相对于arg变量的积分。其余的粗略工作由核心集成算法和基类完成。

现在,我们可以集成包含正弦和余弦函数的简单表达式,如下所示:

再次,可以看到该算法不仅可以处理简单的'sin(x)''cos(x)'函数,而且还可以处理线性参数的函数。该规则已嵌入到核心算法中。不幸的是,我们仍然不能集成非线性参数的函数,例如'sin(x^2)'。我们需要实现特殊的集成方法。这项任务是工作中最复杂、最繁琐的部分。让我们展示一个最简单的集成器类示例,该类可以扩展要集成的表达式的范围。这是该类的代码:

public sealed class ExpandIntegrator : SimplifyIntegrator
{
    protected override BaseExpression Simplify(BaseExpression expr, string vName)
    {
        if (expr is BinaryOperationsExpression)
        {
            BinaryOperationsExpression be = (BinaryOperationsExpression)expr;
            if (be.OperationCount > 0)
            {
                if (be.CanExpand())
                {
                    BaseExpression ee = be.Expand();
                    if (ee != null)
                    {
                        ee.Simplify();
                        return ee;
                    }
                }
            }
        }

        return null;
    }
}

该类继承自'SimplifyIntegrator',并且它试图从集成过程的角度简化表达式。即,该方法扩展了二进制表达式。例如,幂表达式(A*x+B)^2’将简化为以下A^x^2+2*A*B*x+C^2,可以使用内核成功处理算法。实现了该类之后,我们可以集成以下表达式'(A*x+B)^2*(C*x-D)'

类似地,我们可以提供更高级的系统集成方法。它们的实现超出了本文的范围。这里的主要思想是无需修改核心算法即可完成此操作。一旦实现了一种高级集成方法,核心算法便可以使用它,从而扩展了符号集成的功能。

结论

本文介绍了一种使用C#进行符号集成算法的方法。该方法在ANALYTICS库中实现。核心积分算法可实现一元和二元代数运算符和函数的所有积分规则和基本积分公式。通过提供特殊类的后代,可以轻松地将其他高级集成公式和方法引入该算法。系统自动查找高级积分器,并将其用于适当的数学表达式,从而提供新的积分功能。由于该库完全用C#编写,因此不需要其他数学程序包。该库可以轻松地集成到任何.NET应用程序中,从而为科学、工程和教育软件带来符号集成功能。

https://www.codeproject.com/Articles/5293598/Symbolic-Integration-with-Csharp

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值