เพิ่มประสิทธิภาพคอมไพเลอร์สำหรับการเขียนโปรแกรมภาษาโปแลนด์ย้อนกลับง่าย


24

ลักษณะ

จินตภาพการเขียนโปรแกรมภาษา (IPL) ใช้สัญลักษณ์ย้อนกลับโปแลนด์ มันมีคำสั่งดังต่อไปนี้:

  • i - ใส่หมายเลขแล้วดันไปที่สแต็ก
  • o - เอาท์พุทแบบไม่ทำลายด้านบนของสแต็ค (จำนวนยังคงอยู่บนสแต็ก)
  • d - ยกเลิกด้านบนสุดของสแต็ก
  • จำนวนเต็ม - กดตัวเลขนี้ไปที่สแต็ก
  • + - * - ป๊อปอัพตัวเลขสองตัวจากสแต็คดำเนินการที่สอดคล้องกันและผลักดันผลลัพธ์ ไม่มีการแบ่งใน IPL

IPL ทำงานได้เฉพาะกับจำนวนเต็มและใช้สำหรับการคำนวณอย่างง่าย โปรแกรม IPL ถูกเขียนบนหนึ่งบรรทัดและคั่นด้วยช่องว่าง สตริงว่างเปล่าเป็นโปรแกรม IPL ที่ถูกต้อง

โปรแกรม IPL:

i i + o 

ใส่ตัวเลขสองตัวเพิ่มเข้าด้วยกันแล้วส่งผลลัพธ์

จำนวนอินพุตและจำนวนเต็มที่สามารถพุชไปยังสแต็กนั้นอยู่ในช่วง [-999, 999] แต่เอาต์พุตสามารถมีได้ หากภาษาของคุณไม่รองรับตัวเลขขนาดใหญ่ก็ถือว่าใช้ได้

รูปแบบอินพุต / เอาต์พุต

คุณสามารถเลือกรูปแบบอินพุต / เอาท์พุตใดก็ได้ตราบใดที่ชัดเจนในการทำความเข้าใจและอ่าน / เขียน: สตริง, รายการ, โทเค็น ฯลฯ

งาน

คุณได้รับโปรแกรม IPL บางตัวคุณต้องปรับให้เหมาะสม (ลดความยาว):

i 12 + 3 + o d 2 3 + d

หลังจากการเพิ่มประสิทธิภาพจะกลายเป็น

i 15 + o

คุณไม่จำเป็นต้องรักษาสถานะสแต็ก แต่จำนวนอินพุตและเอาต์พุตและคำสั่งซื้อควรตรงกับโปรแกรมดั้งเดิมและที่ปรับให้เหมาะสม

ดังนั้นโปรแกรม IPL:

-40 i * 2 * o i + 3 1 + o i 2 *

หลังจากการเพิ่มประสิทธิภาพจะกลายเป็น

i -80 * o i 4 o i

หรือ

-80 i * o i 4 o i

(โปรดทราบว่าคุณต้องบันทึกอินพุตทั้งหมดแม้ว่าจะไม่เกี่ยวข้องก็ตาม)

ไม่ควรมีการเข้ารหัสฮาร์ดโค้ดสำหรับกรณีทดสอบรหัสควรทำงานกับโปรแกรม IPL ใด ๆ และสร้างโปรแกรม IPL ที่สั้นที่สุดที่เป็นไปได้ที่ตรงตามข้อกำหนด

เกณฑ์การให้คะแนน

การให้คะแนนโค้ดกอล์ฟเริ่มต้น

UPDATE: เปลี่ยนการให้คะแนนเป็นการให้คะแนนโค้ดกอล์ฟแบบบริสุทธิ์ตามคำแนะนำ @Sanchises

กรณีทดสอบ:

การป้อนข้อมูล:

(empty string)

เอาต์พุตที่เป็นไปได้:

(empty string)

การป้อนข้อมูล:

i 4 * 2 + 3 * 6 - o

เอาต์พุตที่เป็นไปได้:

i 12 * o

การป้อนข้อมูล:

1 1 + o

เอาต์พุตที่เป็นไปได้:

2 o

การป้อนข้อมูล:

i 2 + 3 + o d 2 3 + d

เอาต์พุตที่เป็นไปได้:

i 5 + o

การป้อนข้อมูล:

-40 i * 2 * o i + 3 1 + o i 2 *

เอาต์พุตที่เป็นไปได้:

-80 i * o i 4 o i

การป้อนข้อมูล:

i i 1 + i 1 + i 1 + i 1 + d d d d o 

เอาต์พุตที่เป็นไปได้:

i i i i i d d d d o 

การป้อนข้อมูล:

i i i 0 * * * o

เอาต์พุตที่เป็นไปได้:

i i i 0 o

การป้อนข้อมูล:

i i i 1 * * * o

เอาต์พุตที่เป็นไปได้:

i i i * * o

การป้อนข้อมูล:

i 222 + i 222 - + o

เอาต์พุตที่เป็นไปได้:

i i + o

การป้อนข้อมูล:

i 2 + 3 * 2 + 3 * 2 + 3 * i * d i 2 + 3 * i + d i o 2 + 2 - 0 * 1 o

เอาต์พุตที่เป็นไปได้:

i i i i i o 1 o

การป้อนข้อมูล:

i 1 + 2 * 1 + o 

เอาต์พุตที่เป็นไปได้:

i 2 * 3 + o

การป้อนข้อมูล:

1 1 + o i 2 + 3 + o d 2 3 + d 4 i * 2 * o i + 3 1 + o i 2 * i i 1 + i 1 + i 1 + i 1 + d d d d o i i i 0 * * * o i i i 1 * * * o i 2 + i 2 - + o i 2 + 3 * 2 + 3 * 2 + 3 * i * d i 2 + 3 * i + d i o 2 + 2 - 0 * 1 o

เอาต์พุตที่เป็นไปได้:

2 o i 5 + o 8 i * o i 4 o i i i i i i d d d d o i i i 0 o i i i * * * o i i + o i i i i i o 1 o

1
คำถาม: คุณสามารถลดความซับซ้อนของi i d oการi o i(การป้อนข้อมูลที่อยู่ในการสั่งซื้อและการส่งออกที่อยู่ในลำดับ) หรือคุณไม่ควรลดความซับซ้อนของมันได้หรือไม่ (ชุดอินพุตและเอาต์พุตควรเป็นไปตามลำดับ)
Sanchises

1
@ ยกเลิกไม่ได้อินพุตและเอาต์พุตควรเป็นไปตามลำดับ หากโปรแกรมดั้งเดิมป้อนหมายเลข 2 ก่อนที่จะแสดงผลลัพธ์ใด ๆ
АндрейЛомакин

1
ยินดีต้อนรับสู่ PPCG! ความท้าทายแรกที่ดี!
Luis felipe De jesus Munoz

6
จากคิวการตรวจสอบฉันไม่คิดว่าความท้าทายนี้ไม่ชัดเจน หากคุณเป็นเช่นนั้นโปรดแสดงความคิดเห็นเกี่ยวกับสาเหตุ
mbomb007

2
@WW ฉันคิดว่า OP หมายความว่าคุณไม่ควร hardcode เฉพาะกรณีทดสอบที่ระบุไว้ในคำถาม คุณต้องสนับสนุนอินพุตที่กำหนดเอง ไม่ควรมี hardcoding สำหรับกรณีทดสอบโค้ดควรทำงาน กับโปรแกรม IPL ใด ๆ
mbomb007

คำตอบ:


5

ภาษา Wolfram (Mathematica) , 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) ลดความซับซ้อนของรายการคำสั่งเหล่านี้เล็กน้อยและเลือกที่สั้นที่สุดหลังจากแปลงการดำเนินการบางอย่างกลับเป็น subtractions

รหัสนี้ไม่มีประสิทธิภาพมากเพราะมันจะผ่านรายการของพีชคณิตทั้งหมดของการป้อนรหัส สำหรับรหัสอินพุตแบบยาวฉันไม่แนะนำให้เรียกใช้รหัสนี้ แต่ในขณะที่ฉันอ่านมันไม่มีข้อ จำกัด ด้านเวลาหรือหน่วยความจำในการทดสอบนี้

รหัสนี้ทำตามขั้นตอนการปรับให้เหมาะสมหลังจากแปลงการดำเนินการ "-" ทั้งหมดเป็น "+" ด้วยเครื่องหมายพลิกแล้วและในตอนท้ายจะแนะนำตัวดำเนินการ "-" อีกครั้งเมื่อทำการแปลงรหัสกลับเป็นสตริง นี่หมายถึงตัวอย่างเช่น "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 1 + i 1 + i 1 + i 1 + d d d d oดูเหมือนว่านี้จะช่วยให้ข้อผิดพลาดในกรณีทดสอบ
Grimmy

@Grimy อย่างที่ฉันบอกรหัสนี้ไม่ทำงานสำหรับปัญหาใหญ่เพราะต้องผ่านการค้นหาพื้นที่โค้ดที่ละเอียดถี่ถ้วน ข้อผิดพลาดของคุณเป็นความผิดพลาดของหน่วยความจำไม่เพียงพอใน TIO ไม่ใช่เพราะรหัสของฉัน
โรมัน

@Grimy สำหรับ "ii 1 + d o" รหัสของฉันให้ "iid o" ซึ่งฉันคิดว่าเหมาะสมที่สุด สำหรับ "ii 1 + i 1 + dd o" จะให้ "iii + d o" ซึ่งมีโทเค็นจำนวนเดียวกันกับการเพิ่มประสิทธิภาพ "iiidd o" ที่ชัดเจนยิ่งขึ้น ฉันไม่ได้ลองอินพุตอีกต่อไป
โรมัน

ฉันเชื่อว่าอินพุตi 2 * i 2 * + oควรสร้างผลลัพธ์ที่ดีที่สุดi i + 2 * oแต่โค้ดนี้จะคืนค่าอินพุต (ไม่เพิ่มประสิทธิภาพ)
ความซ้ำซ้อน

ขอบคุณ @redundancy มันได้รับการแก้ไขและตัวอย่างของคุณคือหนึ่งในกรณีทดสอบที่รวมอยู่
โรมัน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.