Wolfram语言(数学),733 728 690 564 516个 506 513 548字节
j=Integer;f=Flatten;s=SequenceReplace;A=FixedPoint[f@s[#,{{x_j,p,y_j,t}->{y,t,x*y,p},{x_j,y_j,p}->x+y,{x_j,y_j,t}->x*y,{x_j,p,y_j,p}->{x+y,p},{x_j,t,y_j,t}->{x*y,t},{0,p}|{1,t}->{},{0,t}->{d,0}}]//.{a___,Except[i|o]}->{a}&,#]&;B=Expand@Check[f@FoldPairList[f/@Switch[#2,i,{{i},{#,i@c++}},o,{{Last@#},#},d,{{},Most@#},p,{{},{#[[;;-3]],Tr@#[[-2;;]]}},t,{{},{#[[;;-3]],#[[-2]]*Last@#}},_,{{},{##}}]&,c=0;{},#],x]&;F=MinimalBy[w=A@f[#/.m->{-1,t,p}];z=B@w;s[#,{-1,t,p}->m]&/@A/@Select[Permutations@Join[w,Cases[z /.i@_->i,_j,∞]],B@#==z&],Length][[1]]&
在线尝试!
这是一个四步走法,(1)将“-”替换为“ -1 * +”,这样我们就不必处理减法;(2)稍微简化了命令列表,( 3)列出此命令列表的所有排列,并挑选出在解析(执行)时得出相同结果的命令;(4)将某些操作转换回后,将这些命令列表简化一些,并选择最短的命令减法。
该代码效率极低,因为它会遍历输入代码的所有排列列表。对于长输入代码,我不建议运行此代码。但在我阅读本文时,此挑战没有运行时或内存限制。
此代码在将所有“-”操作转换为带有翻转符号的“ +”操作之后执行优化步骤,并且仅在最后将代码转换回字符串时重新引入“-”运算符。例如,这意味着“ i -1 i * + o”已正确优化为“ ii-o”。
由于I / O格式要求非常宽松,因此该代码将代码作为列表返回,并以列表形式返回,其中符号“ +”,“-”,“ *”分别由p,m,t和标记表示。从字符串到字符串的转换是在TIO上提供的包装函数中完成的:
G[S_] := StringReplace[{"p" -> "+", "m" -> "-", "t" -> "*"}]@StringRiffle@
Quiet@F@
ToExpression[StringSplit[S] /. {"+" -> p, "-" -> m, "*" -> t}]
非高尔夫版本,包括字符串格式包装器,并最小化最终代码字符串的长度而不是令牌的数量,并包括更多的转换技巧:
(* convert code string to list of operators *)
inputfilter[s_] := ToExpression[Flatten[StringSplit[s] /.
{"i" -> i, "o" -> o, "d" -> d, "+" -> p, "-" -> {-1, t, p}, "*" -> t}]]
(* convert list of operators to code string *)
outputfilter[s_] := StringReplace[StringRiffle@Flatten@SequenceReplace[s,
{{-1, t, p} -> m, (* convert "-1 t p" back to "-" *)
{x_ /; x < 0, p} -> {-x, m}, (* convert "y x +" to "y -x -" when x<0 *)
{x_ /; x < 0, t, p} -> {-x, t, m}}], (* convert "y x * +" to "y -x * -" when x<0 *)
{"m" -> "-", "p" -> "+", "t" -> "*"}] (* backsubstitution of symbols *)
(* simplify a list of operators somewhat *)
simplifier[s_] := FixedPoint[Flatten@SequenceReplace[#,
{{x_Integer, p, y_Integer, t} -> {y, t, x*y, p}, (* "x + y *" -> "y * (xy) +" *)
{x_Integer, y_Integer, p} -> x + y, (* "x y +" -> "(x+y)" *)
{x_Integer, y_Integer, t} -> x*y, (* "x y *" -> "(xy)" *)
{x_Integer, p, y_Integer, p} -> {x + y, p}, (* "x + y +" -> "(x+y) +" *)
{x_Integer, t, y_Integer, t} -> {x*y, t}, (* "x * y *" -> "(xy) * *)
{0, p} | {1, t} -> {}, (* "0 +" and "1 *" are deleted *)
{x_Integer, i, p} -> {i, x, p}, (* "x i +" -> "i x +" *)
{x_Integer, i, t} -> {i, x, t}, (* "x i *" -> "i x *" *)
{0, t} -> {d, 0}}] //. (* "0 *" -> "d 0" *)
{a___, Except[i | o]} -> {a} &, s] (* delete trailing useless code *)
(* execute a list of operators and return the list of generated outputs *)
parse[s_] := Expand@Quiet@Check[Flatten@FoldPairList[ (* stack faults are caught here *)
Function[{stack, command}, (* function called for every command*)
Flatten /@ Switch[command, (* code interpretation: *)
i, {{i}, {stack, i[inputcounter++]}}, (* output "i" and add input to stack*)
o, {{stack[[-1]]}, stack}, (* output top of stack *)
d, {{}, Most[stack]}, (* delete top of stack *)
p, {{}, {stack[[;; -3]], stack[[-2]] + stack[[-1]]}}, (* add two stack elements *)
t, {{}, {stack[[;; -3]], stack[[-2]]*stack[[-1]]}}, (* multiply two stack elements*)
_, {{}, {stack, command}}]], (* put number onto stack *)
inputcounter = 0; {}, (* start with zero input counter and empty stack*)
s], (* loop over code list *)
x] (* return "x" if an error occurred *)
(* the main function that takes a code string and returns an optimized code string *)
F[s_] := Module[{w, q},
w = simplifier@inputfilter@s; (* convert input to useful form *)
q = parse[w]; (* execute input code *)
MinimalBy[
outputfilter@*simplifier /@ (* simplify and stringify selected codes *)
Select[Permutations[w], (* all permutations of code list *)
parse[#] == q &], (* select only those that give the correct output *)
StringLength] // Union] (* pick shortest solution by length *)
感谢@redundancy捕获错误:解析器需要一个 Expand
输出应用于输出,以处理分布式等效项。506→513
更新
现在还优化1 o 1 + o
为1 o 2 o
。这是一个非常困难的案例,使代码变慢了很多。513→548
i i d o
为i o i
(输入按顺序,输出按顺序)还是不简化?(一组输入和输出应该有序)