Algorithm 反向波兰符号的简化算法

Algorithm 反向波兰符号的简化算法,algorithm,algebra,simplification,postfix-notation,Algorithm,Algebra,Simplification,Postfix Notation,几天前,我在玩一种深奥的编程语言。Befunge使用后进先出堆栈来存储数据。当您编写程序时,从0到9的数字实际上是将相应值推送到堆栈上的指令。因此,对于exmaple,这将推动7到堆栈: 34+ 为了推送大于9的数字,必须使用小于或等于9的数字进行计算。这将产生123 99*76*+ 在使用Befunge进行求解时,我不得不将相当大的数字999推到堆栈中。在这里,我开始思考如何在尽可能少的指导下完成这项任务。通过用中缀符号写下一个术语,并去掉公因数,我得出了一个结论 9993+*3+* 也可以简

几天前,我在玩一种深奥的编程语言。Befunge使用后进先出堆栈来存储数据。当您编写程序时,从0到9的数字实际上是将相应值推送到堆栈上的指令。因此,对于exmaple,这将推动7到堆栈:

34+

为了推送大于9的数字,必须使用小于或等于9的数字进行计算。这将产生123

99*76*+

在使用Befunge进行求解时,我不得不将相当大的数字999推到堆栈中。在这里,我开始思考如何在尽可能少的指导下完成这项任务。通过用中缀符号写下一个术语,并去掉公因数,我得出了一个结论

9993+*3+*

也可以简单地将两个两位数相乘,产生999,例如

39*66*1+*

我考虑了一会儿,然后决定写一个程序,根据这些规则,对任何给定的整数用反向波兰符号表示最小的表达式。这就是我到目前为止所做的(用NodeJS和下划线写成):

var makeExpr=函数(值){
如果(值<10)返回值+“”;
var输出=”,计数器=0;
(功能fn(val){
计数器++;
如果(val<9){output+=val;return;};
var exp=Math.floor(Math.log(val)/Math.log(9));
var div=数学层(val/Math.pow(9,exp));
_时间(函数(){output+=“9”;});
_(exp-1).times(函数(){output+=“*”;});
如果(div>1)输出+=div+“*”;
fn(价值-数学能力(9,经验)*部门);
})(价值);
_时间(函数(){output+=“+”;});
返回输出。替换(/0\+/,“”);
};
makeExpr(999);
//收益率999**99*3*93*++
这段代码天真地构造了表达式,显然很长。现在我的问题是:

  • 是否有一种算法可以简化反向波兰符号中的表达式
  • 在中缀符号中简化会更容易吗
  • 9993+*3+*
    这样的表达式可以证明是最小的吗

我希望你能提供一些见解。提前感谢。

还有
93*94*1+*
,基本上是
27*37

如果我要解决这个问题,我会首先尝试平均分配这个数字。给定999,我除以9得到111。然后我试着除以9,8,7等等,直到我发现111是3*37

37是素数,所以我贪婪地除以9,得到4和1的余数

这似乎给了我半打我试过的最佳结果。当然,测试均分性有点贵。但可能不会比生成太长的表达式更昂贵

使用此选项,100变为
55*4*
。102计算为
29*5*6+

101提出了一个有趣的案例。101/9 = (9*11) + 2. 或者,交替地,(9*9)+20。让我们看看:

983+*2+  (9*11) + 2
99*45*+  (9*9) + 20
直接生成后缀还是生成中缀和转换更容易,我真的不知道。我可以看到每种方法的优点和缺点

不管怎么说,这就是我要采取的方法:首先尝试平均分配,然后贪婪地除以9。我不知道我会怎么做

一旦你弄明白了,我当然想看看你的解决方案

编辑 这是一个有趣的问题。我提出了一个递归函数,它可以可靠地生成后缀表达式,但它不是最优的。这里是C#

string GetExpression(int val)
{
如果(val<10)
{
返回值ToString();
}
内部,rem;
//首先看看它是否是均匀可除的
对于(int i=9;i>1;--i)
{
quo=数学DivRem(val,i,out rem);
如果(rem==0)
{
//如果val<90,则仅当商
//是一位数字。否则可以表示为
//如(9*x)+y,其中x和y是一位数字。

如果(val>=90 | |)(val<90&&quo还有
93*94*1+*
,基本上是
27*37

如果我要解决这个问题,我会先尝试平均除以这个数字。所以给定999,我会除以9得到111。然后我会尝试除以9,8,7,等等,直到我发现111是3*37

37是素数,所以我贪婪地除以9,得到4和1的余数

对于我尝试过的六个表达式,这似乎给了我最佳的结果。当然,测试整除性有点贵。但可能不会比生成太长的表达式更贵

使用此选项,100变成
55*4*
。102变成
29*5*6+

101带来了一个有趣的例子。101/9=(9*11)+2。或者,(9*9)+20。让我们看看:

983+*2+  (9*11) + 2
99*45*+  (9*9) + 20
直接生成后缀还是生成中缀和转换更容易,我真的不知道。我可以看到它们各自的优点和缺点

不管怎么说,这就是我要采取的方法:首先尝试平均分配,然后贪婪地除以9。我不确定我会如何构造它

一旦你弄明白了,我当然想看看你的解决方案

编辑 这是一个有趣的问题。我提出了一个递归函数,它可以可靠地生成后缀表达式,但它不是最优的。这里是C#

string GetExpression(int val)
{
如果(val<10)
{
返回值ToString();
}
内部,rem;
//首先看看它是否是均匀可除的
对于(int i=9;i>1;--i)
{
quo=数学DivRem(val,i,out rem);
如果(rem==0)
{
//如果val<90,则仅当商
//是一位数字。否则可以表示为
//如(9*x)+y,其中x和y是一位数字。
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;
}
39149*+**
39166*+**
39257*+**
39548*+**
39756*+**
39947*+**
39499**+*
39669**+*
39949**+*
39966**+*
93149*+**
93166*+**
93257*+**
93548*+**
93756*+**
93947*+**
93269**+*
93349**+*
93366**+*
93439**+*
93629**+*
93636**+*
93926**+*
93934**+*
93939+*+*
93948+*+*
93957+*+*
96357**+*
96537**+*
96735**+*
96769+*+*
96778+*+*
97849+*+*
97858+*+*
97867+*+*
99689+*+*
956*99*+*
968*79*+*
39*149*+*
39*166*+*
39*257*+*
39*548*+*
39*756*+*
39*947*+*
329+9677**+**
329+9767**+**
338+9677**+**
338+9767**+**
347+9677**+**
347+9767**+**
356+9677**+**
356+9767**+**
3147789+***+*
31489+77***+*
3174789+***+*
3177489+***+*
3177488*+**+*
$ time ./polish_numbers 999
Result for 999: 39149*+**, length 9

real    0m0.008s
user    0m0.004s
sys     0m0.000s

$ time ./polish_numbers 99999
Result for 99999: 9158*+1569**+**, length 15

real    0m34.289s
user    0m34.296s
sys     0m0.000s
Result for 99999: 9158*+1569**+**, length 15

real    0m31.824s
user    0m31.812s
sys     0m0.012s
Result for 99999: 9158*+1569**+**, length 15, (skipped 36777, computed 26350)
Result for 99999: 1956**+9158*+**, length 15, (skipped 0, computed 34577)

real    0m17.055s
user    0m17.052s
sys     0m0.008s
Result for 99999: 9158*+1569**+**, length 15, (skipped 36777, computed 50000)

real    0m12.058s
user    0m12.048s
sys     0m0.008s
Result for 99999: 97484777**+**+*, length 15, (skipped 36997, computed 33911)

real    0m10.401s
user    0m10.400s
sys     0m0.000s
Result for 999999: 37967+2599**+****, length 17, (skipped 440855)

real    12m55.085s
user    12m55.168s
sys     0m0.028s
Result for 99999: 9158*+1569**+**, length 15

real    0m3.543s
user    0m3.540s
sys     0m0.000s

Result for 999999: 37949+2599**+****, length 17

real    5m51.624s
user    5m51.556s
sys     0m0.068s
$ time ./polish_numbers 99999
Result for 99999: 9158*+1569**+**, length 15

real    0m0.153s
user    0m0.152s
sys     0m0.000s
$ time ./polish_numbers 999999
Result for 999999: 37949+2599**+****, length 17

real    0m3.516s
user    0m3.512s
sys     0m0.004s
$ time ./polish_numbers 9999999
Result for 9999999: 9788995688***+***+*, length 19

real    1m39.903s
user    1m39.904s
sys     0m0.032s
string[] n = new string[10000];
for (int i = 0; i < 10; i++)
    n[i] = "" + i;
for (int i = 10; i < n.Length; i++)
{
    int bestlen = int.MaxValue;
    string best = null;
    // try factors
    int sqrt = (int)Math.Sqrt(i);
    for (int j = 2; j <= sqrt; j++)
    {
        if (i % j == 0)
        {
            int len = n[j].Length + n[i / j].Length + 1;
            if (len < bestlen)
            {
                bestlen = len;
                best = n[j] + n[i / j] + "*";
            }
        }
    }
    // try sums
    for (int j = 1; j < i / 2; j++)
    {
        int len = n[j].Length + n[i - j].Length + 1;
        if (len < bestlen)
        {
            bestlen = len;
            best = n[j] + n[i - j] + "+";
        }
    }
    n[i] = best;
}
string[] n = new string[100000];
int[] biggest_number_of_length = new int[n.Length];
for (int i = 0; i < 10; i++)
    n[i] = "" + i;
biggest_number_of_length[1] = 9;
for (int i = 10; i < n.Length; i++)
{
    int bestlen = int.MaxValue;
    string best = null;
    // try factors
    int sqrt = (int)Math.Sqrt(i);
    for (int j = 2; j <= sqrt; j++)
    {
        if (i % j == 0)
        {
            int len = n[j].Length + n[i / j].Length + 1;
            if (len < bestlen)
            {
                bestlen = len;
                best = n[j] + n[i / j] + "*";
            }
        }
    }
    // try sums
    for (int x = 1; x < bestlen; x += 2)
    {
        int find = i - biggest_number_of_length[x];
        int min = int.MaxValue;
        // find the shortest number that is >= (i - biggest_number_of_length[x])
        for (int k = 1; k < biggest_number_of_length.Length; k += 2)
        {
            if (biggest_number_of_length[k] >= find)
            {
                min = k;
                break;
            }
        }
        // if that number wasn't small enough, it's not worth looking in that range
        if (min + x + 1 < bestlen)
        {
            // range [find .. i] isn't optimal
            for (int j = find; j < i; j++)
            {
                int len = n[i - j].Length + n[j].Length + 1;
                if (len < bestlen)
                {
                    bestlen = len;
                    best = n[i - j] + n[j] + "+";
                }
            }
        }
    }
    // found
    n[i] = best;
    biggest_number_of_length[bestlen] = i;
}