14

What's the best algorithm for evaluating a mathematical expression? I'd like to be able to optimize this a little in the sense that I may have one formula with various variables, which I may need to evaluate hundreds of times using different variables. So basically if I can initially parse the formula so that it is optimized in some way, and I can then pass in the variables to this optimized version as many times as I need, each time it produces a result for me.

I'll be writing this in either Delphi or C#. I have already written something similar by using the shunting yard algorithm, but each time I need to calculate the same formula, I'm having to go through the parsing stage. There must be a better way to do this.

2
  • How complex an expression? Limited to four function arithmetic, or very general? Commented Feb 21, 2009 at 11:10
  • Can get quite complex, including functions with multiple parameters. Commented Feb 21, 2009 at 11:12

2 Answers 2

17

If you want to do it with Delphi, you could look into how the JclExprEval unit works, part of the JEDI Code Library. I wrote it several years ago (it's a little over-engineered); it parses functions and variables and can hand you back a method pointer which evaluates the expression quickly. Pass the variables in by reference, and you can change them directly and the re-evaluated expression will be calculated accordingly.

In any case, the basics of how it works may be helpful for you. Recursive-descent parsing of expressions is easy, and by building a tree you can evaluate multiple times without re-parsing. JclExprEval actually generates code for a simple stack machine, so that it can work a little faster than tree interpretation; stack machines largely restrict their memory operations to arrays and use switches for opcodes, while tree interpretation follows links throughout the heap and often uses virtual dispatch (or double-dispatch) for opcodes, so they usually end up slower.

Taking the same approach as JclExprEval in parsing but written in C#, and building up an Expression, like Marc suggests, is another perfectly valid approach. The JIT-compiled expression ought to be quite a bit faster than an interpreted expression program or tree, which themselves are a lot faster than parsing.

Sign up to request clarification or add additional context in comments.

1 Comment

That looks like just what I need. thks. I like 'over-engineered'!
8

In C# with .NET 3.5, you can use Expression for this; you can build a parameterised expression and then compile it to a delegate. This is exactly what I did for the maths aspect of Finguistics. I still have the parsing code I used if you want it...

The main trick I used was that to keep the delegate type known, I used an array as the input type - treating different args as arr[0], arr1, arr[2] etc. This means I could compile to (for example) a Func<decimal[], decimal> (takes an array of decimals, returns a decimal).

Once you have called Compile(), this is pertty much as though you had code to do it directly.

(edit)

As a brief example of using Expression in this way (with a hard-coded function), see below. The parser I have already written currently works as a predicate checker - i.e. to check that "? + (2 * ? - ?) = 22 + ?" - but it wouldn't be hard to change it to return the result instead (and introduce more operations, like sin/pow/etc - presumably by mapping them directly to public methods on a helper object (via Expression.Call)).

using System;
using System.Linq.Expressions;
static class Program
{
    static void Main()
    {
        var args = Expression.Parameter(typeof(float[]), "args");
        var x = Expression.ArrayIndex(args, Expression.Constant(0));
        var y = Expression.ArrayIndex(args, Expression.Constant(1));
        var add = Expression.Add(x, y);
        var lambda = Expression.Lambda<Func<float[], float>>(add, args);

        Func<float[], float> func = lambda.Compile();
        Console.WriteLine(func.Call(1, 2));
        Console.WriteLine(func.Call(3, 4));
        Console.WriteLine(func.Call(5, 6));
    }

    static T Call<T>(this Func<T[], T> func, params T[] args)
    { // just allows "params" usage...
        return func(args);
    } 
}

2 Comments

I'm now wondering whether to do a "proper" job of the parsing code as a pet project... it wouldn't be much change to the existing code.
@boj - well, that was over a year ago ;-p Pretty bog-standard parser, IIRC

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.