65.9K
CodeProject 正在变化。 阅读更多。
Home

VB.NET 中的多项式数学解析器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (10投票s)

2022 年 4 月 3 日

MIT

3分钟阅读

viewsIcon

17540

downloadIcon

1275

VB.NET 中的多项式数学解析器和计算器

引言

在许多情况下,可能存在一个包含数学表达式的 string,例如“1+2*5”、“(3+i)(3-i)”或“(z^2*w+4z^3w-w^2-4z*w^2)/(w+4z*w)”,并且需要进行数学运算并计算结果。也许我们需要计算不同变量值的结果,结果可以是另一个多项式。在这些情况下,这里介绍的多项式解析器可能会有所帮助。

背景

这里的类是我 完全免费且可下载的 CAS 计算器 的一小部分 - 但有所改进。其中一个目标是这些类不依赖于 CAS 计算器中使用的其他“外部”类。

七个 类

  • 类 'Msg10' 仅包含一些用于处理可能错误的邮件。

  • 类 'G10' 包含全局成员,例如正则表达式模式。
  • 类 'Rational' 在运算中提供了更高的精度。

  • 类 'Complex' 进行复数运算。

  • 类 'Polynomial' 运算多项式。

  • 类 'Roots' 查找多项式的根和因子。
  • 类 'parsePolynomial' 负责将输入字符串分成令牌,并相应地调用 RootsPolynomialMsg10 类。 Roots 依赖于 Polynomial 类,而 Polynomial 类又依赖于 Complex 类,Complex 类又依赖于 Rational 类。 '令牌化'工作由正则表达式模式完成。

令牌

令牌组是

numbers <num>
operators <op>
logical operators <lop>
functions <fn>
functions <vfn> functions retreiving a complex or polynomial array
constants <cnt>
variables <var>
any other character <any>
besides end of tokens <end> formed by an escape character Chr(27).

模式看起来像

(?s)(?<num>((\d{1,3}((\,\d{3})+(?!\d))(\.[0-9]+)?)|[\.\d]+)([eE](\s*)[-+]?[0-9]+)?)(?-s)|
(?<op>[-+*/\^])|
(?<lop>nand|mod|and|nor|xor|not|or|%|!)(?![a-zA-Z_]+)|
(?i)(?<fn>logtwo|logten|acosh|acoth|acsch|asech|asinh|atanh|floor|round|norm|conj|
coth|csch|sech|acos|acot|acsc|asec|asin|atan|cosh|sign|sinh|sqrt|tanh|abs|cos|cot|
csc|exp|log|sec|sin|sqr|tan|ln|re|im(?-i))(?![a-zA-Z_]+)|
(?i)(?<vfn>roots|(?<fct>factorize|factors|factor)(?-i))(?![a-zA-Z_]+)|
(?<cnt>e|(?i)pi(?-i))(?![a-zA-Z_]+)|
\(|\)|
(?<vars>\w)+|(?<end>\e)+|
(?<any>[^\s←\,\.])+|(?<any>\,|\.)+

例如,尾随的 '(?![a-zA-Z_]+)' 在 <cnt> 中允许使用 'epsilon' 或 'cosA' 等变量,而不会干扰常量、逻辑运算符和函数。

函数 roots 返回一个包含输入多项式根的复数数组。函数 factor 返回输入多项式的因子数组。输入也可以是 (分子多项式)/(分母多项式) 的形式。 

Using the Code

有两种可能的实例化方式

    Dim eP As New ParsePolynomial
    eP.CultureInfo = New Globalization.CultureInfo("fr-FR")
    ...
    Dim eP As New ParsePolynomial(New Globalization.CultureInfo("es-AR"))
    ...

默认情况下,CultureInfo 设置为“en-US”。

评估是通过调用两个 Evaluate() 方法之一完成的。

第一种方法

    '// argument is a string:
    Dim poly As Polynomial = eP.Evaluate("(x-1)*(x+1)")
    ...

第一个方法使用变量,在 Dictionary(Of String, Polynomial) 中设置

    Dim eP As New ParsePolynomial
    eP.vars.Add("x", New Polynomial(Complex.one))
    Dim polyA As New Polynomial("a", 2) '// 'a' variable, 2 exponent (a^2)
    polyA += New Polynomial(-1.0) ' // a ^ 2 - 1
    eP.vars.Add("y", polyA)
   '// argument is a string:
   Dim cplx As Complex = eP.Evaluate("x*(y-i*5)")
    ...

解析 string 后,可以调用重载的第二个方法

    '// change "x" value (change any variable value):
     eP.vars.Item("x") = New Polynomial(3)
    '// argument is the Dictionary(Of String, Polynomial):
    Dim polyC As Polynomial = eP.Evaluate(eP.vars)
   ...

变量名以字母或下划线 (_) 开头,可以包含字母、数字或下划线,并且可以为任意长度。

当然,如果您不需要解析器,您可以直接调用 Polynomial 类。

输出

输出可以只是一个 Polynomial,一个 Complex() 数组,或者 Polynomial() 数组

    Dim t1 As Int64 = Now.Ticks
    Dim p As Polynomial = eP.Evaluate(TBExpression.Text)
    Dim t2 As New TimeSpan(Now.Ticks - t1)
    If eP.vPoly.Length Then ' Response is an array of Polynomials ?
        Dim s1 As String = ""
        For i As Int32 = 0 To eP.vPoly.Length - 1
            If eP.vPoly(i) IsNot Nothing Then
                s1 += eP.vPoly(i).ToStringPolynomial(eP.Decimals, eP.Imaginary, eP.CultureInfo) + "</br>"
            Else
                s1 += "-------------------</br>"
            End If
        Next
        NavigateToString(s1) ' Show in Webbrowser1
    ElseIf eP.vRoots.Length Then  ' Response is an array of Complex ?
        Dim s1 As String = ""
        For i As Int32 = 0 To eP.vRoots.Length - 1
            s1 += eP.vRoots(i).ToStringComplex(eP.Decimals, eP.Imaginary, eP.CultureInfo) + "</br>"
        Next
        NavigateToString(s1) ' Show in Webbrowser1
    Else  ' Response is a Polynomial
        NavigateToString(p.ToStringPolynomial(eP.Decimals, eP.Imaginary, eP.CultureInfo))
    End If

调用 eP.Evaluate("factor(numerator)/(denominator)") 时,结果将保存在  eP.vPoly() 中,并且一个空元素将把因子与分子和分母分开。

您可以调用 Polynomial.ToString()ToStringPolynomial(Optional numDecimals As Int32 = 15, Optional sImg As String = "i", Optional cultureInfo As Globalization.CultureInfo = Nothing) As String

   ...
   polyC.ToStringPolynomial(4, eP.Imaginary, eP.CultureInfo)
   ...

详细版本

详细版本将通过在前面加上“detail”一词来显示操作步骤。

基本原理

解析方法是递归下降解析:通过递归下降解析表达式

评估方法 E 为任何加法或减法调用 T,但 T 首先为任何乘法或减法调用 F,而 F 首先为任何可能的幂运算调用 PP 首先调用 v 以获取下一个令牌。 如果存在“(”令牌,则 v 递归调用 T

    E --> T {( "+" | "-" ) T}
    T --> F {( "*" | "/" ) F}
    F --> P ["^" F]
    P --> v | "(" E ")" | "-" T 

分步演示

这里介绍的算法将 TF 合并到一个方法中。此外,方法 v 运算任何可能的函数,如 cos()csc()mod 等等。

在撰写本文时,我发现了一些小故障。如果您发现任何其他错误,请告诉我。

历史

  • 2022 年 4 月 3 日:初始版本
© . All rights reserved.