Search code examples
algorithmalgebrasimplificationpostfix-notation

Simplification Algorithm for Reverse Polish Notation


A couple of days ago I played around with Befunge which is an esoteric programming language. Befunge uses a LIFO stack to store data. When you write programs the digits from 0 to 9 are actually Befunge-instructions which push the corresponding values onto the stack. So for exmaple this would push a 7 to stack:

34+

In order to push a number greater than 9, calculations must be done with numbers less than or equal to 9. This would yield 123.

99*76*+

While solving Euler Problem 1 with Befunge I had to push the fairly large number 999 to the stack. Here I began to wonder how I could accomplish this task with as few instructions as possible. By writing a term down in infix notation and taking out common factors I came up with

9993+*3+*

One could also simply multiply two two-digit numbers which produce 999, e.g.

39*66*1+*

I thought about this for while and then decided to write a program which puts out the smallest expression according to these rules in reverse polish notation for any given integer. This is what I have so far (written in NodeJS with underscorejs):

var makeExpr = function (value) {
    if (value < 10) return value + "";
    var output = "", counter = 0;
    (function fn (val) {
        counter++;
        if(val < 9) { output  += val; return; };
        var exp = Math.floor(Math.log(val) / Math.log(9));
        var div = Math.floor(val / Math.pow(9, exp));
        _( exp ).times(function () { output += "9"; });
        _(exp-1).times(function () { output += "*"; });
        if (div > 1) output += div + "*";
        fn(val - Math.pow(9, exp) * div);    
    })(value);
    _(counter-1).times(function () { output+= "+"; });
    return output.replace(/0\+/, "");
};

makeExpr(999);
// yields 999**99*3*93*++

This piece of code constructs the expression naively and is obvously way to long. Now my questions:

  • Is there an algorithm to simplify expressions in reverse polish notation?
  • Would simplification be easier in infix notation?
  • Can an expression like 9993+*3+* be proofed to be the smallest one possible?

I hope you can give some insights. Thanks in advance.


Solution

  • There's also 93*94*1+*, which is basically 27*37.

    Were I to attack this problem, I'd start by first trying to evenly divide the number. So given 999 I would divide by 9 and get 111. Then I'd try to divide by 9, 8, 7, etc. until I discovered that 111 is 3*37.

    37 is prime, so I go greedy and divide by 9, giving me 4 with a remainder of 1.

    That seems to give me optimum results for the half dozen I've tried. It's a little expensive, of course, testing for even divisibility. But perhaps not more expensive than generating a too-long expression.

    Using this, 100 becomes 55*4*. 102 works out to 29*5*6+.

    101 brings up an interesting case. 101/9 = (9*11) + 2. Or, alternately, (9*9)+20. Let's see:

    983+*2+  (9*11) + 2
    99*45*+  (9*9) + 20
    

    Whether it's easier to generate the postfix directly or generate infix and convert, I really don't know. I can see benefits and drawbacks to each.

    Anyway, that's the approach I'd take: try to divide evenly at first, and then be greedy dividing by 9. Not sure exactly how I'd structure it.

    I'd sure like to see your solution once you figure it out.

    Edit

    This is an interesting problem. I came up with a recursive function that does a credible job of generating postfix expressions, but it's not optimum. Here it is in C#.

    string GetExpression(int val)
    {
        if (val < 10)
        {
            return val.ToString();
        }
        int quo, rem;
        // first see if it's evenly divisible
        for (int i = 9; i > 1; --i)
        {
            quo = Math.DivRem(val, i, out rem);
            if (rem == 0)
            {
                // If val < 90, then only generate here if the quotient
                // is a one-digit number. Otherwise it can be expressed
                // as (9 * x) + y, where x and y are one-digit numbers.
                if (val >= 90 || (val < 90 && quo <= 9))
                {
                    // value is (i * quo)
                    return i + GetExpression(quo) + "*";
                }
            }
        }
    
        quo = Math.DivRem(val, 9, out rem);
        // value is (9 * quo) + rem
        // optimization reduces (9 * 1) to 9
        var s1 = "9" + ((quo == 1) ? string.Empty : GetExpression(quo) + "*");
        var s2 = GetExpression(rem) + "+";
        return s1 + s2;
    }
    

    For 999 it generates 9394*1+**, which I believe is optimum.

    This generates optimum expressions for values <= 90. Every number from 0 to 90 can be expressed as the product of two one-digit numbers, or by an expression of the form (9x + y), where x and y are one-digit numbers. However, I don't know that this guarantees an optimum expression for values greater than 90.