正文

Parse Math expressions, find roots in VB2008-07-24 19:55:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/iamben250/37049.html

分享到:

Parse Math expressions, find roots in VB


' Visual Basic 6 module
'-------------------------- Math.bas ---------------------------
' Math expression parser that supports +, -, *, /, ^ (power),
' @ (logarithm), functions (exp, ln, sin, cos, tan, asin, acos,
' atan, sinh, cosh, tanh, int, frac, abs, sqrt), brackets
' Validate expression syntax, evaluate functions, derive functions
' Find roots using bisection method and Newton method
'-------------------------- Math.bas ---------------------------
' Copyright Ionut Alex. Chitu, http://deadline.3x.ro
'
' Public functions:
' Function Error_Message(ByVal N As Integer) As String
'
' 	N = Code number
' 	Returns the associated error message
'
' Sub Check_Syntax(ByVal S As String, ByVal WithX as Boolean,
'              ByRef Function_Error As Integer, ByRef i1 As Integer)
'
' 	S = function text
' 	WithX = can contain x variable?
' 	Function_Error = error code, 0=no error
' 	i1 = position in function text of the error
'
' Function Evaluate(ByVal S As String, ByVal x As Double,
'                  ByRef Function_Error As Integer) As Double
'
' 	S = function text
' 	x = value of the variable
' 	Function_Error = error code, 0=no error
' 	Returns the result of evaluation
'
' Function Derive (ByVal S As String, ByVal X As Double,
'                  ByRef Function_Error As Integer) As Double
'
' 	S = function text
' 	X = value of the variable
' 	Function_Error = error code, 0=no error
' 	Returns the result of numerical derivative
'
' Function Find_root_Newton(ByVal S As String, ByVal x0 As Double, _
'                  ByVal eps As Double, ByRef FErr As Integer) As Double
'
' 	S = function text
' 	x0 = estimation of the root
' 	eps = precision of the root
' 	FErr = error code, 0=no error
' 	Returns the root close to x0
'
' Function Find_root_bisection(ByVal S As String, ByVal a As Double, _
'                  ByVal b As Double, ByVal eps As Double, _
'                  ByRef Err As Integer) As Double
'
' 	S = function text
' 	a, b = interval where to search the root
' 	eps = precision of the root
' 	Err = (out) error code, 0=no error
' 	Returns a root that lies in [a, b]
'
'
' Example (copy-paste this code in a form load event in VB6):
' Dim fun as String, Err as Integer, pos as Integer
' Dim x as Double, y as Double
' fun="sin(x)+2*cos(x^2)"
' x=0
' Check_Syntax fun, True, Err, pos
' If Err=0 then
'   y=Evaluate(fun, x, Err)
'   If Err=0 then
'      MsgBox "f(" & CStr(x) & ")=" & CStr(y), vbInformation, fun
'   Else
'      MsgBox Error_Message(Err)
'   End If
' Else
'   MsgBox Error_Message(Err)   
' End If
'
'-------------------------------------------------------------------
Option Explicit 'always use this option in vb
Private Const Pi As Double = 3.14159265358979
Private Const e  As Double = 2.71828182845905
Private argX As String 
Private Const evNumber = 1
Private Const evConstant = 2
Private Const evFunction = 5
Private Const evLeftBracket = 6
Private Const evRightBracket = 7
Private Const evOperand = 8
Private Const evElse = 16

Public Function Error_Message(n As Integer) As String
	Select Case n
		Case 0
		Error_Message = "No error !"
		Case 1
		Error_Message = "Error evaluating power function."
		Case 2
		Error_Message = "Error evaluating logarithm. The argument must be > 0."
		Case 3
		Error_Message = "Error evaluating tangent."
		Case 4
		Error_Message = "Error evaluating square root. The argument must be >= 0."
		Case 5
		Error_Message = "Error evaluating arcsine. The argument must be in [-1, 1]."
		Case 6
		Error_Message = "Error evaluating arccosine. The argument must be in [-1, 1]."
		Case 7
		Error_Message = "Division by zero."
		Case 11
		Error_Message = "Unexpected end of parenthesis."
		Case 12
		Error_Message = "Operators must be followed by number, constant, function."
		Case 13
		Error_Message = "Function must be followed by parenthesis."
		Case 14
		Error_Message = "An error occured in evaluation"
		Case 15
		Error_Message = "Expression expected."
		Case 16
		Error_Message = "Unknown function."
		Case 17
		Error_Message = "Enter the value for which you want to evaluate the function."
		Case 18
		Error_Message = "An error occured in evaluation"
	End Select
End Function

Private Function IsNum(ByVal s As String) As Boolean
	IsNum = IsNumeric(s) Or (s = ".") Or (s = ",")
End Function

Private Function Lexical_Unit(ByVal c As String) As Integer
	c = LCase$(c)
	If IsNum(c) Then
		Lexical_Unit = evNumber
	Else
		Select Case c
			Case argX, "e", "pi"
			Lexical_Unit = evConstant
			Case "ln", "abs", "exp", "sqrt", "int", "frac", _
			"sin", "cos", "tan", "asin", "acos", "atan", _
			"sinh", "cosh", "tanh"
			Lexical_Unit = evFunction
			Case "("
			Lexical_Unit = evLeftBracket
			Case ")"
			Lexical_Unit = evRightBracket
			Case "+", "-", "*", "/", "^", "@"
			Lexical_Unit = evOperand
			Case Else
			Lexical_Unit = evElse
		End Select
	End If
End Function

Public Sub Next_Token(ByVal S As String, ByVal k As Integer, _
ByRef i1 As Integer, ByRef i2 As Integer)
Dim s1 As String, s2 As String
Dim other_token As Boolean
	Do While Mid$(S, k, 1) = " "
		k = k + 1
	Loop
	i1 = k
	s1 = LCase$(Mid$(S, k, 1))
	Do
		s2 = LCase$(Mid$(S, k + 1, 1))
		other_token = (s1 <> s2) And Not (IsNum(s1) And IsNum(s2)) And _
		Not ((s1 >= "a") And (s1 <= "z") And (s2 >= "a") And (s2 <= "z"))
		k = k + 1
		s1 = s2
	Loop Until other_token Or ((s1 = "(") And (s2 = "(")) Or ((s1 = ")") And (s2 = ")"))
	i2 = k - 1
End Sub

Public Sub Check_Syntax(ByVal S As String, ByVal WithX As Boolean, _
ByRef Function_Error As Integer, ByRef i1 As Integer)
Dim one As Integer, two As Integer, brackets As Integer
Dim k As Integer, i2 As Integer, Si As String, Sk As String
	i1 = 2
	i2 = 1
	argX = IIf(WithX, "x", "")
	Function_Error = IIf(Trim(S) = "", 17, 0)
	If Function_Error = 17 Then Exit Sub
	S = "(" + S + ")"
	Si = "("
	k = i2 + 1
	one = 6 ' Lexical_Unit ("(")
	brackets = 1
	Do While k <= Len(S)
		Next_Token S, k, i1, i2
		Sk = Mid$(S, i1, i2 - i1 + 1)
		k = i2 + 1
		two = Lexical_Unit(Sk)
		If two = 16 Then
			Function_Error = 16
			Exit Sub
		End If
		Select Case Sk
			Case "(": brackets = brackets + 1
			Case ")": brackets = brackets - 1
		End Select
		If brackets < 0 Then
			Function_Error = 11
			Exit Sub
		End If
		Select Case one
			Case evNumber
			If Not ((two = evConstant) Or (two = evLeftBracket) Or _
			(two = evRightBracket) Or (two = evOperand)) Then Function_Error = 12
			Case evConstant
			If Not ((two = evLeftBracket) Or (two = evRightBracket) Or (two = evOperand)) Then Function_Error = 12
			Case evFunction
			If (two <> evLeftBracket) Then Function_Error = 13
			Case evLeftBracket
			If (two = evRightBracket) Or ((two = evOperand) And (Sk <> "-")) Then Function_Error = 14
			Case evRightBracket
			If (two <> evOperand) And (two <> evRightBracket) Then Function_Error = 15
			Case evOperand
			If (two = evRightBracket) Or (two = evOperand) Then Function_Error = 14
			Case evElse
			Function_Error = 16
		End Select
		If Function_Error > 0 Then Exit Sub
		Si = Sk
		one = two
	Loop
	If brackets <> 0 Then Function_Error = 11
End Sub

Private Function Priority(ByVal c As String) As Integer
Dim P As Integer
	c = Left$(c, 1)
	Select Case c
		Case "(", ")": P = 0		'Priority 0 : brackets
		Case "+", "-": P = 1		'Priority 1 : aditive operators
		Case "*", "/": P = 2		'Priority 2 : multiplicative operators
		Case "^", "@": P = 3		'Priority 3 : exponential operators (a @ b -> log(a), base=b)
		Case "a" To "z", "A" To "Z": P = 4  'Priority 4 : math functions, constants
	End Select
	Priority = P
End Function

Public Function Evaluate(ByVal S As String, ByVal x As Double, _
ByRef Function_Error As Integer) As Double
' parse expression and evaluate function                        
On Error GoTo Overflow
Dim opd(200) As Double, op(200) As String
Dim top1 As Integer, top2 As Integer, i As Integer, k As Integer
Dim value As Double, x1 As Double, x2 As Double
Dim i1 As Integer, i2 As Integer, Si As String, Sk As String, S0 As String
	Function_Error = 0
	argX = "x"
	top1 = 0
	top2 = 1
	op(top2) = "("
	S = S + ")"
	i = 1
	Si = "("
	Do While (i <= Len(S)) And (top2 > 0)
		Next_Token S, i, i1, i2
		Sk = Si
		Si = Mid$(S, i1, i2 - i1 + 1)
		i = i2 + 1
		If IsNum(Si) Then
		top1 = top1 + 1
		opd(top1) = Val(Replace$(Si, ",", "."))
		value = opd(top1)
		Else
		Select Case LCase$(Si)
		Case "(":  top2 = top2 + 1: op(top2) = "("
		Case argX, "pi", "e":
		top1 = top1 + 1
		S0 = LCase$(Si)
		Select Case S0
		Case argX: opd(top1) = x
		Case "pi": opd(top1) = Pi
		Case "e": opd(top1) = e
		End Select
		If IsNum(Sk) Then
		top2 = top2 + 1
		op(top2) = "*"
		End If
		Case Else
		Do While (top2 > 0) And (op(top2) <> "(") And (op(top2) <> ")") _
		And (Priority(op(top2)) >= Priority(Si))
			If top1 >= 2 Then x1 = opd(top1 - 1)
			x2 = opd(top1)
			Select Case LCase$(op(top2))
			Case "+": value = x1 + x2
			Case "-": value = x1 - x2
			Case "~": value = -x2  'unary minus
			Case "*": value = x1 * x2
			Case "/":
			If x2 <> 0 Then
			value = x1 / x2
			Else: Function_Error = 7: Evaluate = 0: Exit Function
			End If
			Case "^":
			If ((x1 < 0) And (x2 <> Int(x2)) Or _
			((x1 = 0) And (x2 = 0))) Then
			Function_Error = 1: Evaluate = 0: Exit Function
			Else: value = x1 ^ x2
			End If
			Case "@": 'logarithm
			If (x1 > 0) And (x2 > 0) And (x2 <> 1) Then
			value = Log(x1) / Log(x2)
			Else
			If x2 = 1 Then
			value = Log(x1)
			Else: Function_Error = 2: Evaluate = 0: Exit Function
			End If
			End If
			Case "sin": value = Sin(x2)
			Case "cos": value = Cos(x2)
			Case "tan":
			If Cos(x2) <> 0 Then
			value = Tan(x2)
			Else: Function_Error = 3: Evaluate = 0: Exit Function
			End If
			Case "asin":
			If (x2 > -1) And (x2 < 1) Then
			value = Atn(x2 / Sqr(1 - x2 * x2))
			ElseIf x2 = -1 Then
			value = -Pi / 2
			ElseIf x2 = 1 Then
			value = Pi / 2
			Else: Function_Error = 5: Evaluate = 0: Exit Function
			End If
			Case "acos":
			If (x2 > 0) And (x2 <= 1) Then
			value = Atn(Sqr(1 - x2 * x2) / x2)
			ElseIf (x2 < 0) And (x2 >= -1) Then
			value = Atn(Sqr(1 - x2 * x2) / x2) + Pi
			ElseIf x2 = 0 Then
			value = Pi / 2
			Else: Function_Error = 6: Evaluate = 0: Exit Function
			End If
			Case "atan": value = Atn(x2)
			Case "sinh": value = (Exp(x2) - Exp(-x2)) * 0.5
			Case "cosh": value = (Exp(x2) + Exp(-x2)) * 0.5
			Case "tanh": value = (Exp(x2) - Exp(-x2)) / (Exp(x2) + Exp(-x2))
			Case "ln":
			If x2 > 0 Then
			value = Log(x2)
			Else: Function_Error = 2: Evaluate = 0: Exit Function
			End If
			Case "exp": value = Exp(x2)
			Case "abs": value = Abs(x2)
			Case "sqrt":
			If x2 >= 0 Then
			value = Sqr(x2)
			Else: Function_Error = 4: Evaluate = 0: Exit Function
			End If
			Case "int": value = Int(x2)
			Case "frac": value = x2 - Int(x2)
			End Select
			If ((LCase$(Mid$(op(top2), 1, 1)) < "a") Or _
			(LCase$(Mid$(op(top2), 1, 1)) > argX)) _
			And (op(top2) <> "~") Then top1 = top1 - 1
			opd(top1) = value
			top2 = top2 - 1
		Loop
		If top2 > 0 Then
			If (op(top2) <> "(") Or (Si <> ")") Then
			top2 = top2 + 1
			op(top2) = IIf((Sk = "(") And (Si = "-"), "~", Si)
		Else
			top2 = top2 - 1
		End If
		End If
		End Select
		End If
	Loop
	Evaluate = opd(1)
	Exit Function
	Overflow:
	Function_Error = 18 'overflow
	MsgBox "overflow"
End Function

Public Function Derive (ByVal S As String, ByVal x As Double, _
ByRef Function_Error As Integer) As Double
' ================================================================
'          f(x-2h)-8f(x-h)+8f(x+h)-f(x+2h)
' f'(x)  = -------------------------------    (derivative formula)
'                      12 h
' =================================================================
Dim v1 As Double, v2 As Double, v3 As Double, v4 As Double
Dim FErr As Integer
Const h as Double = 0.000456
	Derive = 0
	Function_Error = 1
	v1 = Evaluate(S, x - 2 * h, FErr)
	If FErr <> 0 Then Exit Function
	v2 = Evaluate(S, x - h, FErr)
	If FErr <> 0 Then Exit Function
	v3 = Evaluate(S, x + h, FErr)
	If FErr <> 0 Then Exit Function
	v4 = Evaluate(S, x + 2 * h, FErr)
	If FErr <> 0 Then Exit Function
	Function_Error = 0
	Derive = (v1 - 8 * v2 + 8 * v3 - v4) / (12 * h)
End Function

Function Find_root_Newton(ByVal S As String, ByVal x0 As Double, _
ByVal eps As Double, FErr As Integer) As Double
' solve equations using Newton method
On Error Resume Next
Dim x1 As Double, v1 As Double, v2 As Double
Dim FErr1 As Integer, FErr2 As Integer
Dim steps As Integer
	FErr = 0
	x1 = x0
	steps = 0
	Do
		x0 = x1
		v1 = Evaluate(S, x0, FErr1)
		v2 = Derive(S, x0, FErr2)
		If (FErr1 * FErr2 > 0) Or (v2 = 0) Then
			Find_root_Newton = 0
			FErr = 1
			Exit Function
		Else
			x1 = x0 - v1 / v2
		End If
		steps = steps + 1
	Loop Until (Abs(x1 - x0) < eps) Or (steps = 100)
	If Evaluate(S, x1, FErr1) < eps Then
		Find_root_Newton = x1
	Else
		Find_root_Newton = 0
		FErr = 2
	End If
End Function

Function Find_root_bisection(ByVal S As String, ByVal a As Double, _
ByVal b As Double, ByVal eps As Double, _
ByRef Err As Integer) As Double
' solve equations using bisection method
On Error Resume Next
Dim ya As Double, yc As Double, c As Double
Dim steps As Integer, FErr As Integer
	Err = 0
	Do
		c = (a + b) / 2
		ya = Evaluate(S, a, FErr)
		yc = Evaluate(S, c, FErr)
		If ya = 0 Then
			Find_root_bisection = a
			Exit Function
		ElseIf yc = 0 Then
			Find_root_bisection = c
			Exit Function
		End If
		If ya * yc < 0 Then
			b = c
		Else
			a = c
		End If
	Loop Until (Abs(b - a) < eps)
	Find_root_bisection = c
End Function

阅读(2402) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册