|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Contents
IntroductionIn this article, I describe how we can parse a mathematical expression and identify its elements. I chose a simple application of a symbolic differentiation as an example, to add more value to the article and to give maximum benefit to the readers. The application, as in the previous figure, includes a simple grid to draw an input curve and its differentiation in the same view. I will not dig into the mathematics, as that is out of the article scope. I will just include a definition of the derivative in the next section. If you are not interested in the math, you can skip it. Differentiation DefinitionThe derivative of a function can be interpreted either as a function whose value at d(f(x))/dx,
or if y = f(x), dy/dx
Differentiation Formulas
Mathematical ExpressionA mathematical expression includes numbers, operators, functions, variables and operators. Operators include Expression examples:
sin(2*x)/x
sin(45+cos(2)/tan(x)
sign(cos(x)
32*9-8/2
Differentiation StepsExpression Parsing to Fill StackExpression parsing is the process of scanning an expression to find its elements (numbers, operators, functions and variables). Each operator should have two operands; for example, take 2*12. The operator " Expression execution depends on operator precedence. If we can construct a stack of operators and operands such that each operator is followed by its two operands, 2*12 should construct this stack: "*, "2", "12". So, the calculation function checks the stack. If the item is an operator, then it takes its two operands and does the calculation for them in a recursion formula, so the whole expression can be calculated. For example, the expression: sin(2*12)/7+9^2
...should be calculated in these steps:
The function that does the scanning takes the input expression and an array of operands depending on their precedence, as in the following code: ///////////////////////////////////////////////////////////
// GetOperator: scan the input string
// to search for any of the input operators
///////////////////////////////////////////////////////////
int GetOperator(IN LPCSTR lpcs, IN LPCSTR lpcsOperators[])
{
for(int nIndex = 0; lpcsOperators[nIndex]; nIndex++)
{
int nOpen = 0;
// scan the expression from its end
LPSTR p = (LPSTR)lpcs+strlen(lpcs)-1;
// loop tells reach expression start
while(p >= lpcs)
{
// check for close
if(*p == ')')
nOpen++;
// check for open
else if(*p == '(')
nOpen--;
// check for operator
else if(nOpen == 0 && strchr(lpcsOperators[nIndex], *p) != NULL)
// check if the operator in not a sign mark
if((*p != '-' && *p != '+') ||
(p != lpcs && IsRightSign(*(p-1),
lpcsOperators, nIndex+1)))
// return operator index
return (int)(p-lpcs);
p--;
}
}
// operator not found
return -1;
}
It is obvious now that it is a recursion operation. To do that, we need to formulate the expression in a stack of operators and operands, and process that stack in a certain way to get the final result. The main steps to do that in brief are:
If we apply the previous steps, we will have the stack in this form:
To describe that in more detail, let us have a look at the code that parses the expression and fills the stack: bool FillStack(LPCSTR lpcsInput, vector<EXPRESSIONITEM*>& vStack)
{
// operators array from high to low
priority LPCSTR lpcsOperators[] = { "+-", "*/", "^%", NULL };
// insert first input into the stack
vStack.push_back(new ExpressionItem(lpcsInput));
// loop in Expression stack to check if
// any Expression can be divided to two queries
for(int nIndex = 0; nIndex < (int)vStack.size(); nIndex++)
// check if Expression item is operator
if(vStack[nIndex]->m_cOperator == 0)
{
// copy Expression string
CString str = vStack[nIndex]->m_strInput;
// parse expression to find operators
int nOpIndex = GetOperator(str, lpcsOperators);
if(nOpIndex != -1)
{
// split the Expression into
// two queries at the operator index
vStack[nIndex]->m_cOperator = str[nOpIndex];
// add the left operand of the
// operator as a new expression
vStack.insert(vStack.begin()+nIndex+1,
new ExpressionItem(str.Left(nOpIndex)));
// add the right operand
// of the operator as a new expression
vStack.insert(vStack.begin()+nIndex+2,
new ExpressionItem(str.Mid(nOpIndex+1)));
}
else
// check if Expression string
// starts with function or parenthesis
if((vStack[nIndex]->m_nFunction = GetFunction(str,
vStack[nIndex]->m_nSign)) == 0
&& vStack[nIndex]->m_nSign == 0)
// remove parentheses and re-scan the Expression
vStack[nIndex--]->m_strInput =
str.Mid(1, str.GetLength()-2);
}
return true;
}
No one can ever deny now that this is the simplest code you can find about mathematical expression parsing. We can collect its comments in points, as follows:
Apply Differentiation Formulas on the StackApplying differentiation formulas means to iterate the stack, take each operator and apply the differentiation rule - depending on the operator - on the two operands of the operator:
If any operand is an operator, then the function is called again in a recursive way to differentiate the whole expression. The following code represents this function: CString DifferentiateStack(vector<EXPRESSIONITEM*>& vStack, int& nExpression)
{
ExpressionItem *pQI = vStack[nExpression++];
if(pQI->m_cOperator)
{
// get left operand
CString u = vStack[nExpression]->GetInput();
// get left operand differentiation
CString du = DifferentiateStack(vStack, nExpression);
// get right operand
CString v = vStack[nExpression]->GetInput();
// get right operand differentiation
CString dv = DifferentiateStack(vStack, nExpression);
if(du == '0') // u is constant
...
else if(dv == '0') // v is constant
...
else
switch(pQI->m_cOperator)
{
case '-': // d(u-v) = du-dv
case '+': // d(u+v) = du+dv
pQI->m_strOutput = '('+du+pQI->m_cOperator+dv+')';
break;
case '*': // d(u*v) = u*dv+du*v
pQI->m_strOutput = '('+u+'*'+dv+'+'+du+'*'+v+')';
break;
case '/': // d(u/v) = (du*v-u*dv)/v^2
pQI->m_strOutput = '('+du+'*'+v+'-'+u+'*'+dv+")/("+v+")^2";
break;
case '^': // d(u^v) = v*u^(v-1)*du+u^v*ln(u)*dv
pQI->m_strOutput = '('+v+'*'+u+"^("+v+"-1)*"+ du+
'+'+u+'^'+v+"*ln("+u+")*"+dv+')';
break;
}
}
else
// get Expression differentiation
pQI->GetDifferentiation();
// return resultant differentiation
return pQI->m_strOutput;
}
Optimize EquationOptimizing the equation includes these simple steps:
Differentiation Pseudo CodeDifferentiate(input)
{
Stack = FillStack(input)
output = DifferentiateStack(Stack)
Optimize(output)
return output
}
FillStack(input)
{
operators[] { "+-", "*/", "^%" }
stack.push(input)
loop( n = 1 to stack.size() )
{
if stack[n] is not operator
if GetOperator(stack[n],
operators) success
{
Split stack[n]
stack.Insrt(left operand)
stack.Insrt(right operand)
}
else
GetFunction(stack[n])
}
}
DifferentiateStack(stack, index)
{
if stack[index] is operator
{
index++
u = stack[index]
du = DifferentiateStack(stack, index)
v = stack[index]
dv = DifferentiateStack(stack, index)
if operator = '-' or '+'
output = du+operator+dv
else if operator = '*'
output = u*dv+du*v
else if operator = '/'
output = (du*v-u*dv)/v^2
else if operator = '^'
output = v*u^(v-1)*du+u^v*ln(u)*dv
}
else
output = stack[index++].GetDifferentiation()
return output
}
void Optimize(str)
{
replace "--" with "" or "+"
replace "+-" with "-"
replace "((....))" with "(....)"
remove any 1*
remove any *1
remove any exponent equal 1
remove unneeded parentheses
if str changed then
Optimize(str)
}
ExpressionItem::GetDifferentiation()
{
if Function then
{
arument = Argument(input);
if function = SIN
output = Differentiate(arument)*cos(arument)
else if function = COS
output = Differentiate(arument)*(-sin(arument))
else
...
}
}
else
{
if input = "x"
output = "1"
else if input = "-x"
output = "-1"
else if input is numeric
output = "0"
else
output = "d"+input+"/dx"
}
}
Program TabsDifferentiationThe Differentiation tab displays the differentiation results after applying the differentiation steps in the input expression. The input expression is calculated before differentiation to optimize any arithmetic part that includes numbers as in the example:
Differentiation Stack
CalculationThis tab is a Symbolic Calculator that can calculate any mathematical expression, however complicated. The calculation includes only two constants: e = 2.7182818284590452353602874713527
pi = 3.1415926535897932384626433832795
Calculation StackThis view is used to view the stack of the calculation details.
DrawThis tab is used to view the input function curve and its differentiation curve. You can view the input function curve only by clicking "Draw." The view includes horizontal and vertical axes, as well as grid lines. You move over any point in the view to see its coordinates in the left upper corner, and you can zoom in on any part by selecting it with the mouse. The horizontal and vertical axes' limits update automatically to include the input function points in the view. You can change these limits by right clicking the view.
HelpThis tab just displays a list of allowed functions in the application.
Points of Interest
Updates
References
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||