Search code examples
c#equation-solving

C# equation solver


I have the following equation

var x = 1.0175d;
var spread = 0.0525d;
var duration = 6.36019306689938d;

var result = spread - (x / 100 - 1) / duration;

// result = 0.208128137

however I'm struggling to solve the equation for x where we start from the result.

0.0525 - ( x / 100 - 1) / 6.36019306689938 = 0.208128137

The following equation solver returns the correct result, and shows a step-by-step, however I cant follow how to turn this into C# as it relies on the notion of x whilst simplifying the equation.

https://www.mathpapa.com/equation-solver/?q=0.0525-%28x%2F100-1%29%2F6.36019306689938%3D0.208128137


Solution

  • To solve an expression such as spread - (x / 100 - 1) / duration = result for x using Math.NET Symbolics:

    Firstly use NuGet to add MathNet.Symbolics to your project.

    Next, to solve an equation you need to rewrite it in normalised form so that it equals zero:

    spread - (x / 100 - 1) / duration - result = 0

    (In this case we just subtracted the right hand side of the equation from both sides, which by definition results in the expression being equal to zero.)

    Then we can define the expression to solve for x like so:

    var expression = Infix.Parse("spread - (x / 100 - 1) / duration - result").ResultValue;

    Now we need to write a little helper method to solve a simple root (courtesy of /u/Christoph Rüegg):

    public static Expression SolveSimpleRoot(Expression variable, Expression expr)
    {
        // try to bring expression into polynomial form
        Expression simple = Algebraic.Expand(Rational.Numerator(Rational.Simplify(variable, expr)));
    
        // extract coefficients, solve known forms of order up to 1
        Expression[] coeff = Polynomial.Coefficients(variable, simple);
    
        return coeff.Length switch {
            1 => Expression.Zero.Equals(coeff[0]) ? variable : Expression.Undefined,
            2 => Rational.Simplify(variable, Algebraic.Expand(-coeff[0] / coeff[1])),
            _ => Expression.Undefined
        };
    }
    

    Now we can call SolveSimpleRoot() to solve the expression, and then evaluate that solution by supplying all the spread, duration and result parameters like so:

        var solution = SolveSimpleRoot(Expression.Symbol("x"), expression);
    
        Dictionary<string, FloatingPoint> values = new() {
            ["spread"]   = 0.0525,
            ["duration"] = 6.36019306689938,
            ["result"]   = 0.208128137
        };
    
        var answer = Evaluate.Evaluate(values, solution).RealValue;
    

    Putting that all together into a complete console app:

    using MathNet.Symbolics;
    
    namespace Net8Console;
    
    static class Program
    {
        public static void Main()
        {
            var expression = Infix.Parse("spread - (x / 100.0 - 1.0) / duration - result").ResultValue;
            var solution   = SolveSimpleRoot(Expression.Symbol("x"), expression);
    
            Dictionary<string, FloatingPoint> values = new() {
                ["spread"]   = 0.0525,
                ["duration"] = 6.36019306689938,
                ["result"]   = 0.208128137
            };
    
            var answer = Evaluate.Evaluate(values, solution).RealValue;
            Console.WriteLine(answer);
        }
    
        public static Expression SolveSimpleRoot(Expression variable, Expression expr)
        {
            // try to bring expression into polynomial form
            Expression simple = Algebraic.Expand(Rational.Numerator(Rational.Simplify(variable, expr)));
    
            // extract coefficients, solve known forms of order up to 1
            Expression[] coeff = Polynomial.Coefficients(variable, simple);
    
            return coeff.Length switch {
                1 => Expression.Zero.Equals(coeff[0]) ? variable : Expression.Undefined,
                2 => Rational.Simplify(variable, Algebraic.Expand(-coeff[0] / coeff[1])),
                _ => Expression.Undefined
            };
        }
    }