ตัวกระจายรังสีเมตาดาต้า


19

พื้นหลัง

ในเว็บไซต์นี้บางครั้งเรามีคำถามที่ต้องการให้โปรแกรม "แข็งรังสี" ซึ่งหมายความว่าโปรแกรมจะต้องสามารถอยู่รอดในการลบหนึ่งไบต์หรือมากกว่าไม่ว่าจะลบไบต์ใด

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

งาน

เขียนสองโปรแกรมหรือฟังก์ชั่น E (ตัวเข้ารหัส) และ D (ตัวถอดรหัส) เช่นนั้น:

  • E จะใช้เวลาสองขัดแย้งลำดับของ octets (ซึ่งเราจะเรียกว่า " การป้อนข้อมูล " ในข้อกำหนดนี้) และติดลบ " รังสี " และผลลำดับของ octets ที่ " เข้ารหัส ";
  • D รับหนึ่งอาร์กิวเมนต์ลำดับของ octets (" encdng ") และเอาต์พุตลำดับของ octets " reconstruction ";
  • หากคุณใช้ทั้ง E และ D (กับencdng , การป้อนข้อมูลเพื่อ D ได้รับการแต่งตั้งโดยการลบไม่เกินรังสีองค์ประกอบจากการเข้ารหัส (ไม่จำเป็นต้องติดกัน)) แล้วการฟื้นฟูจะเท่ากับการป้อนข้อมูลเรื่องที่ตัวละครถูกลบออกไปยังแบบฟอร์มencdng

ชี้แจง

  • หากคุณส่งฟังก์ชั่นคุณไม่จำเป็นต้องเรียกมันEและD; คุณสามารถเลือกชื่อที่เหมาะสมที่สุดสำหรับภาษาของคุณ
  • "octet" นั้นเป็นจำนวนเต็มตั้งแต่ 0 ถึง 255 ซึ่งคุณสามารถเข้ารหัสเป็นจำนวนเต็มอักขระหรืออะไรก็ตามที่เหมาะสมกับภาษาของคุณ
  • E และ D จะต้องถูกกำหนดทั้งหมด (เช่นให้อินพุตเดียวกันจะสร้างเอาต์พุตเดียวกันเสมอโดยที่ "อินพุต" ถูกกำหนดเป็นอินพุตและการฉายรังสีสำหรับ E หรือencdngสำหรับ D) โดยเฉพาะอย่างยิ่ง E อาจไม่สื่อสารข้อมูลกับ D ผ่านช่องทางด้านข้าง
  • การลบจะดำเนินการโดยการลบองค์ประกอบหนึ่งของลำดับ; คิดว่าการเปิด seuqence ในตัวแก้ไขวางเคอร์เซอร์ที่ตำแหน่งใดก็ได้และกด Backspace หากองค์ประกอบปรากฏหลายครั้งอาจเป็นไปได้ว่าจะมีการลบองค์ประกอบหนึ่งสำเนาเท่านั้น (เช่นกรณีอื่น ๆ ของ octet เดียวกันจะไม่ได้รับผลกระทบ)
  • แม้ว่าคะแนนจะถูกคำนวณเพียงบนพื้นฐานของความเป็นธรรมสั้นการป้อนโปรแกรมของคุณจะต้องทำงานในทางทฤษฎีสำหรับการใด ๆการป้อนข้อมูลและการฉายรังสี โดยเฉพาะอย่างยิ่งต้องทำงานเรื่องที่ปรากฏใน octets ไม่มีการป้อนข้อมูล (ขออภัยผู้ที่ต้องการความสามารถในการใช้อักขระที่ไม่สามารถพิมพ์ได้ซึ่งพวกเขารู้ว่าจะไม่ปรากฏในอินพุต แต่ฉันต้องแน่ใจว่าข้อมูลที่ป้อนไม่ได้ของอินพุทเพื่อให้ความท้าทายเกี่ยวกับการชุบแข็งรังสีมากกว่าการบีบอัด)
  • คุณสามารถส่งไฟล์เดียวที่กำหนดสองฟังก์ชั่น สองไฟล์ที่แต่ละคนกำหนดฟังก์ชั่นหรือที่ทั้งสองโปรแกรมเต็มรูปแบบ; หรือสามไฟล์สองไฟล์ที่ใช้ D และ E ตามลำดับ (ไม่ว่าจะเป็นโปรแกรมเต็มรูปแบบหรือผ่านการกำหนดฟังก์ชั่น) และไฟล์ที่สามซึ่งเป็นไฟล์ส่วนหัวหรือไลบรารีที่ใช้ร่วมกันทั้ง D และ E การใช้ภาษาการเขียนโปรแกรมของคุณจะต้องสามารถเข้าใจทั้งสองโปรแกรมโดยไม่มีข้อโต้แย้งเพิ่มเติมเช่นที่ตั้งไฟล์ (หรือคุณต้องจ่ายค่าปรับเป็นจำนวนไบต์สำหรับการเรียกใช้งานของคุณในลักษณะที่ผิดปกติตามกฎมาตรฐานของเรา)

เงื่อนไขชัยชนะ

สำหรับแต่ละระยะเวลาและการฉายรังสีให้F ( ความยาว , การฉายรังสี ) เป็นความยาวรวมของการเข้ารหัส s ตรงที่ทุกการป้อนข้อมูลที่มีความยาวความยาวและได้รับการฉายรังสี (นั่นคือF ( ความยาว , การฉายรังสี ) = ผลรวมการป้อนข้อมูลมีความยาวยาวยาว (E ( การป้อนข้อมูล , การฉายรังสี )).) แล้วให้กรัม ( ความยาว , การฉายรังสี ) เท่ากับF ( ความยาว ,รังสี ) ÷ 256 ความยาว กล่าวอีกนัยหนึ่งgคือความยาวเฉลี่ยของเอาต์พุตที่เข้ารหัสสำหรับความยาวที่กำหนดของอินพุตและความต้องการการชุบแข็งด้วยรังสีที่กำหนด (ในทางทฤษฎีคุณสามารถคำนวณสิ่งนี้ได้โดยใช้กำลังดุร้าย แต่อาจใช้เวลานานกว่าจะคิดคะแนนของคุณในแบบนั้นฉันคาดว่าการส่งผลงานส่วนใหญ่จะสามารถโต้แย้งทางคณิตศาสตร์ได้ว่าคะแนนของพวกเขาเป็นอย่างไรถ้าคุณ ไม่แน่ใจโพสต์คะแนนโดยประมาณและคุณหรือคนอื่นสามารถคำนวณได้ในเชิงลึกยิ่งขึ้นหากรายการอื่นโพสต์คะแนนที่คล้ายกัน)

คะแนนของคุณเท่ากับผลรวมของg ( ความยาว , การแผ่รังสี ) สำหรับการแผ่รังสีทั้งหมดในช่วง 0 ถึง 9 และความยาวทั้งหมดในช่วง 0 ถึง 99 รวม, บวก (ส่วนใหญ่เพื่อหลีกเลี่ยงการเข้ารหัสแบบแข็งหรือเพื่อให้การแข่งขันดำเนินต่อไป บางคนค้นพบการเข้ารหัสที่สมบูรณ์แบบทางคณิตศาสตร์นี่น่าจะเป็นปัจจัยที่น้อยที่สุดมิฉะนั้นจำนวนรวมของไบต์ที่คุณส่งต่อความท้าทาย (รวมถึงบทลงโทษมาตรฐานสำหรับสิ่งต่าง ๆ เช่นต้องการล่ามธงผิดปกติหรือชื่อไฟล์เฉพาะ) ผู้ชนะคือรายการที่มีคะแนนต่ำสุด (tiebroken โดยรายการแรกที่จะส่ง)


ตัวถอดรหัสอาจทราบพารามิเตอร์ของรังสีด้วยหรือไม่
orlp

(หรือยาวแต่ผมเชื่อว่าการเรียนรู้ก็ควรจะให้คุณอื่น ๆ สำหรับรูปแบบมากที่สุด)
orlp

1
@orlp: ไม่มันมีแค่สตริงเท่านั้น ในปัญหาเรื่องการแผ่รังสีที่รุนแรงตัวถอดรหัส (เช่นภาษา) ไม่ทราบกฎการแผ่รังสีที่ใช้ดังนั้นตัวถอดรหัสของคุณจะไม่รู้จักพวกมันเช่นกัน มันต้องอนุมานพวกเขาจากการป้อนข้อมูลของมัน

จากวิดีโอคอมพิวเตอร์นี้ฉันจะสร้างภาษาที่ใช้โค้ดในรูปแบบ 3 ไบต์สามส่วน: ถ้ามันไม่ตรงกันทั้งหมดมีบางอย่างผิดปกติและเรามีข้อมูลเพียงพอที่จะเข้าใจว่าค่าที่ถูกต้องคืออะไร อาจเป็นวิธีที่ทำได้โดยใช้บิตน้อยลงแต่ฉันไม่มีสมองที่จะรู้วิธีในตอนนี้
Draco18s

คุณจะรวมส่วนหัวและโปรแกรมได้อย่างไร
CalculatorFeline

คำตอบ:


8

CJam, คะแนน≤ 286,516 + 54 + 36 = 286,606

Encoder

{_{1\+_:e>)_0a+@@b0\{{)_256b_,\e`,-}g}*256b+\)e*}{*}?}

ลองออนไลน์!

ถอดรหัส

{_{e`1f=(\256b{256b_,\e`,=},,\b1>}&}

ลองออนไลน์!

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

แนวคิดพื้นฐานสำหรับการสร้างการเข้ารหัสการแผ่รังสีมีสองขั้นตอน:

  1. ค้นหาการเข้ารหัสที่ไม่เคยมี octet เหมือนกันสองรายการติดต่อกัน
  2. ทำซ้ำแต่ละออคเต็ตในสตริงที่เข้ารหัสr + 1ครั้งโดยที่rคือระดับรังสี

ด้วยวิธีนี้การแผ่รังสีไม่สามารถลบอักขระที่เหมือนกันหนึ่งตัวเพื่อให้เราสามารถถอดรหัสสตริงได้โดยนำอักขระหนึ่งตัวจากการวิ่งแต่ละครั้งแล้วถอดรหัสขั้นตอนที่ 1

ดังนั้นส่วนที่น่าสนใจเพียงอย่างเดียวคือการค้นหาการเข้ารหัสที่ไม่เคยให้ผลออคเต็ตซ้ำ แนวคิดพื้นฐานคือการใช้บางอย่างเช่นA043096เป็นระบบตัวเลข นั่นคือการเข้ารหัสจำนวนเต็มNเราแค่นับในฐานข้ามตัวเลขทั้งหมดด้วย octets ซ้ำ ฉันเชื่อว่าจำนวนของตัวเลขที่สามารถแสดงด้วยวิธีนี้ได้ถึงdหลักเท่ากับจำนวนของตัวเลขที่สามารถแสดงในฐาน bijective b-1 (ตั้งแต่เมื่อคุณต้องการเขียนตัวเลขคุณสามารถ เลือกระหว่างตัวเลขb-1สำหรับแต่ละตำแหน่งโดยไม่ละเมิดข้อ จำกัด )

แน่นอนที่จะได้รับการบีบอัดสูงสุดเราจะใช้ข = 256 ในการเปลี่ยนอินพุตให้เป็นจำนวนเต็มเราสามารถใช้การแปลงฐานได้เช่นกัน หากฉันไม่ขี้เกียจฉันจะใช้ฐาน bijective สำหรับอินพุต แต่ตอนนี้ฉันเพิ่งเตรียม1(เพื่อให้แน่ใจว่าไม่มีศูนย์นำหน้า) จากนั้นใช้ฐานที่เล็กที่สุดเท่าที่เป็นไปได้ซึ่ง octet ทั้งหมดใน อินพุตมีค่าน้อยกว่าฐาน

จากนั้นเบสนี้จะถูกผนวกเข้ากับการเข้ารหัส (เพื่อให้ตัวถอดรหัสรู้ว่าจะใช้เบสตัวใด) และแยกออกจากตัวเลขที่เหลือด้วย 0-octet (ใช้งานได้เพราะตัวเลขที่เหลือจะไม่เริ่มต้นด้วยศูนย์) ในฐานะที่เป็นการเพิ่มประสิทธิภาพเล็กน้อยสตริงว่างยังคงเป็นสตริงว่าง

เหตุผลที่ฉันยังไม่ได้คำนวณคะแนนที่แน่นอนข้างต้นคือฉันแค่คำนวณขอบเขตบนสำหรับระยะเวลาที่อินพุตแต่ละรายการจะขึ้นอยู่กับความยาวและอ็อกเท็ตสูงสุดของมัน อย่างไรก็ตามสำหรับพารามิเตอร์ทั้งสองนี้มักจะมีความยาวเอาท์พุทที่แตกต่างกันสองแบบและฉันยังไม่ได้คิดหาจุดที่จุดเปลี่ยนระหว่างพวกเขาเกิดขึ้น ฉันยังใช้ความยาวของฐานปกติ 255 แทนที่จะเป็นฐาน bijective 255 เพื่อประมาณความยาวนั้นซึ่งใหญ่กว่านั้นอีกเล็กน้อย รหัส Mathematica ที่ฉันใช้ในการคำนวณมีดังต่อไปนี้:

num[l_, 1] = 0;
num[l_, base_] := num[l, base] = base^l - Sum[num[l, b], {b, base - 1}]
Sum[
  num[l, b]*(r + 1)*(2 + IntegerLength[2*b^l - 1, 255])/256^l, 
  {r, 0, 9}, {l, 1, 99}, {b, 2, 256}
]
N@%

num[l, b]ควรให้จำนวนสตริงที่lมีความยาวoctet สูงสุดb-1(ยกเว้นb == 1ที่ฉัน hardcoded มัน0เพราะฉันมักจะใช้ฐานอย่างน้อย2)


"สมมติว่าโดยเฉลี่ยแล้วสตริงที่มีความยาว N ไม่สามารถเข้ารหัสในเวลาน้อยกว่า (r + 1) * N octets ที่ระดับรังสี r" ฉันไม่เห็นเหตุผลว่าสิ่งนี้จะเป็นจริง ฉันไม่แปลกใจเลยที่จะเห็นรูปแบบการเข้ารหัสที่มีอยู่ซึ่งก็คือ O (N + r)
orlp

1
@ หรือไม่ฉันไม่เห็นว่ามันจะเป็นไปได้ แต่ฉันรอคอยที่จะได้รับการพิสูจน์ผิด :)
Martin Ender

ความคิดดี. ฉันไม่รู้ CJam แต่จากคำอธิบายของคุณดูเหมือนว่าคุณกำลังเตรียมฐานไปยังข้อมูลที่เข้ารหัส หากเป็นเช่นนั้นจะมีปัญหาหากมีการลบตัวอักขระออกจากข้อมูลที่ได้เตรียมไว้หรือไม่ (นั่นเป็นความผิดพลาดที่ฉันทำที่ @Leo ชี้ให้เห็นว่าฉันต้องแก้ไขในการแก้ปัญหาของฉัน)
Mitchell Spector

@MitchellSpector ฐานจะถูกเติมก่อนที่จะทำซ้ำแต่ละตัวอักษร r + 1 ครั้ง ดังนั้นฐานจึงปลอดภัยจากรังสีเช่นกัน
Martin Ender

นั่นเป็นสิ่งที่ดี - นั่นคือสิ่งที่ฉันทำลงไปในวิธีแก้ปัญหาของฉันเช่นกัน คุณเพียงแค่ต้องแน่ใจว่าตัวถอดรหัสสามารถถอดรหัสข้อมูลที่ถูกเตรียมไว้ก่อนที่จะรู้ว่าฐานคืออะไร
Mitchell Spector

6

bash + โปรแกรมอรรถประโยชน์ GNU, คะแนน294506 283468

แก้ไข 1: แก้ไขปัญหาที่ @Leo สังเกต - ขอบคุณ!

แก้ไข 2: ปรับปรุงวิธีการเข้ารหัสสำหรับพารามิเตอร์รังสีเพื่อให้ได้คะแนนที่ดีขึ้น

ตัวเข้ารหัส (97 ไบต์):

for((j=0;j<$1;j++)){ p+=p\;;}
(sed 's/\(.\)\1/\1a\1a/g'<<<$1;cat)|xxd -p -c1|sed ${p-;}|xxd -r -p

ถอดรหัส (121 ไบต์):

read n
n=`sed 's/\(.\)\1*/\1/g'<<<$n`
sed -r "s/( ..)\1{0,${n//a}}/\1/g"<<<' 0a '`xxd -p -c1`|sed 's/^ [^ ]*//'|xxd -r -p

สำหรับตัวเข้ารหัส: ลำดับ Octet ส่งผ่านเป็นอักขระใน stdin พารามิเตอร์รังสี r ถูกส่งผ่านเป็นอาร์กิวเมนต์

สำหรับตัวถอดรหัส: อินพุตถูกส่งเป็นอักขระใน stdin

สำหรับทั้งคู่: เอาต์พุตเมื่อ stdout

เครื่องเข้ารหัสจะป้อนข้อมูลตัวเลขของ r โดยป้อนอักขระ 'a' ระหว่างแต่ละคู่ของตัวเลขที่เหมือนกันตามลำดับตามด้วยบรรทัดใหม่หนึ่งบรรทัด จากนั้นคัดลอกอินพุตทั้งหมด (เริ่มต้นด้วยอักขระที่เพิ่มขึ้น) แทนที่แต่ละอักขระด้วย r + 1 สำเนาของอักขระนั้น

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

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


การคำนวณคะแนน:

สมมติว่าอินพุตมีความยาว L และพารามิเตอร์การแผ่รังสีคือ r (ซึ่งมากที่สุดคือ 9 สำหรับการคำนวณเกณฑ์การให้คะแนนดังนั้นจึงเหมาะกับตัวเลขหนึ่งหลักจึงไม่มีตัวอักษรซ้ำ ๆ กันติดต่อกัน) ข้อมูลที่ถูกสรุปคือ 2 ไบต์ (หลักขึ้นบรรทัดใหม่) ดังนั้นเอาต์พุตคือ (r + 1) (L + 2) ไบต์สำหรับสตรีมที่เข้ารหัส

ดังนั้น g (L, r) = (r + 1) (L + 2)

มันเป็นไปตามที่คะแนนรวมสามารถคำนวณได้เป็น

ป้อนคำอธิบายรูปภาพที่นี่


เกิดอะไรขึ้นถ้า octet ที่ถูกดร็อปเป็นอันแรก? ตัวถอดรหัสไม่จำเป็นต้องrอ่าน
Leo

@Leo คุณพูดถูก ฉันจะดูซ่อมพรุ่งนี้ - มันสายเกินไปคืนนี้ ขอบคุณที่จำได้
Mitchell Spector

@Leo ฉันคิดว่ามันแก้ไขได้ด้วยการรวม r + 1 สำเนาของแต่ละหลักของ r ตามด้วย r + 1 ขึ้นบรรทัดใหม่ หากถูกต้องคะแนนจะไม่เพิ่มขึ้นมากนัก
Mitchell Spector

สิ่งที่ควรจะได้ผล ฉันคิดว่าคุณควรใช้มาตรการเพิ่มเติมเพื่อให้แน่ใจว่ามันทำงานได้อย่างถูกต้องกับการแผ่รังสีที่สูงขึ้น (เช่นการแผ่รังสี222) แต่โชคดีที่คะแนนนั้นคำนวณจากการแผ่รังสี 0-9 เท่านั้นดังนั้นมันจะไม่ได้รับผลกระทบมากนัก PS ฉันกำลังคิดที่จะใช้การเข้ารหัสแบบเดียวกันนี้นั่นเป็นสาเหตุที่ฉันพบข้อผิดพลาดทันที)
Leo

@Leo ใช่การแก้ไขนั้นใช้ได้กับค่าทั้งหมดของการแผ่รังสีแม้ว่าคะแนนจะคำนึงถึงค่าการแผ่รังสีสูงสุดที่ 9 เท่านั้น
Mitchell Spector

3

Perl + Math :: {ModInt, Polynomial, Prime :: Util}, คะแนน≤ 92819

$m=Math::Polynomial;sub l{($n,$b,$d)=@_;$n||$d||return;$n%$b,l($n/$b,$b,$d&&$d-1)}sub g{$p=$m->interpolate([grep ref$_[$_],0..$map{$p->evaluate($_)}0..$}sub p{prev_prime(128**$s)}sub e{($_,$r)=@_;length||return'';$s=$r+1;s/^[␀␁]/␁$&/;@l=map{mod($_,p$s)}l(Math::BigInt->from_bytes($_),p$s);$@l+$r>p($s)&&return e($_,$s);$a=0;join'',map{map{chr$_+$a}l($_->residue,128,$s,($a^=128))}g(@l)}sub d{@l=split/([␀-␡]+)/,$_[0];@l||return'';$s=vecmax map length,@l;@l=g map{length==$s&&mod($m->new(map{ord()%128}split//)->evaluate(128),p$s)}@l;$$_=$m->new(map{$_->residue}@l)->evaluate(p$s)->to_bytes;s/^␁//;$_}

รูปภาพควบคุมใช้เพื่อแสดงอักขระควบคุมที่สอดคล้องกัน (เช่นเป็นอักขระ NUL ตัวอักษร) ไม่ต้องกังวลกับการพยายามอ่านรหัส มีรุ่นที่อ่านเพิ่มเติมได้ด้านล่าง

-Mbigint -MMath::ModInt=mod -MMath::Polynomial -MNtheory=:allทำงานด้วย -MMath::Bigint=lib,GMPไม่จำเป็น (และไม่รวมอยู่ในคะแนน) แต่ถ้าคุณเพิ่มก่อนห้องสมุดอื่น ๆ มันจะทำให้โปรแกรมทำงานได้เร็วขึ้น

การคำนวณคะแนน

อัลกอริทึมที่นี่ค่อนข้างจะแก้ไขไม่ได้ แต่จะค่อนข้างยากที่จะเขียน (เนื่องจาก Perl ไม่ได้มีห้องสมุดที่เหมาะสม) ด้วยเหตุนี้ฉันจึงทำการแลกเปลี่ยนขนาด / ประสิทธิภาพในรหัสบนพื้นฐานที่กำหนดว่าสามารถบันทึกไบต์ในการเข้ารหัสได้จึงไม่มีประเด็นที่จะพยายามกำจัดทุกจุดจากการเล่นกอล์ฟ

โปรแกรมประกอบด้วยรหัส 600 ไบต์บวก 78 โทษสำหรับตัวเลือกบรรทัดคำสั่งให้โทษ 678 จุด ส่วนที่เหลือของคะแนนคำนวณโดยใช้โปรแกรมในกรณีที่ดีที่สุดและกรณีที่เลวร้ายที่สุด (ในแง่ของความยาวเอาท์พุท) สตริงสำหรับทุกความยาวจาก 0 ถึง 99 และทุกระดับรังสีจาก 0 ถึง 9; กรณีเฉลี่ยอยู่ที่ใดที่หนึ่งระหว่างและสิ่งนี้ให้ขอบเขตกับคะแนน (มันไม่คุ้มค่าที่จะพยายามคำนวณค่าที่แน่นอนยกเว้นว่ามีรายการอื่นเข้ามาด้วยคะแนนที่คล้ายกัน)

นี่จึงหมายความว่าคะแนนจากประสิทธิภาพการเข้ารหัสอยู่ในช่วง 91100 ถึง 92141 รวมดังนั้นคะแนนสุดท้ายคือ:

91100 + 600 + 78 = 91778 ≤คะแนน≤ 92819 = 92141 + 600 + 78

เวอร์ชันที่ไม่ค่อยตีกอล์ฟพร้อมความคิดเห็นและรหัสทดสอบ

นี่คือโปรแกรมดั้งเดิม + การขึ้นบรรทัดใหม่การเยื้องและความคิดเห็น (อันที่จริงแล้วเวอร์ชัน golfed ถูกสร้างขึ้นโดยลบบรรทัดใหม่ / การเยื้อง / ความคิดเห็นออกจากเวอร์ชันนี้)

use 5.010;                    # -M5.010; free
use Math::BigInt lib=>'GMP';  # not necessary, but makes things much faster
use bigint;                   # -Mbigint
use Math::ModInt 'mod';       # -MMath::ModInt=mod
use Math::Polynomial;         # -MMath::Polynomial
use ntheory ':all';           # -Mntheory=:all
use warnings;                 # for testing; clearly not necessary

### Start of program

$m=Math::Polynomial;          # store the module in a variable for golfiness

sub l{ # express a number $n in base $b with at least $d digits, LSdigit first
    # Note: we can't use a builtin for this because the builtins I'm aware of
    # assume that $b fits into an integer, which is not necessarily the case.
    ($n,$b,$d)=@_;
    $n||$d||return;
    $n%$b,l($n/$b,$b,$d&&$d-1)
}

sub g{ # replaces garbled blocks in the input with their actual values
    # The basic idea here is to interpolate a polynomial through all the blocks,
    # of the lowest possible degree. Unknown blocks then get the value that the
    # polynomial evaluates to. (This is a special case of Reed-Solomon coding.)
    # Clearly, if we have at least as many ungarbled blocks as we did original
    # elements, we'll get the same polynomial, thus we can always reconstruct
    # the input.
    # Note (because it's confusing): @_ is the input, $_ is the current element
    # in a loop, but @_ is written as $_ when using the [ or # operator (e.g.
    # $_[0] is the first element of @_.
    # We waste a few bytes of source for efficiency, storing the polynomial
    # in a variable rather than recalculating it each time.
    $p=$m->interpolate([grep ref$_[$_],0..$#_],[grep ref,@_]);
    # Then we just evaluate the polynomial for each element of the input.
    map{$p->evaluate($_)}0..$#_
}

sub p{ # determines maximum value of a block, given (radiation+1)
    # We split the input up into blocks. Each block has a prime number of
    # possibilities, and is stored using the top 7 bits of (radiation+1)
    # consecutive bytes of the output. Work out the largest possible prime that
    # satisfies this property.
    prev_prime(128**$s)
}

sub e{ # encoder; arguments: input (bytestring), radiation (integer)
    ($_,$r)=@_; # Read the arguments into variables, $_ and $r respectively
    length||return''; # special case for empty string
    $s=$r+1; # Also store radiation+1; we use it a lot
    # Ensure that the input doesn't start with NUL, via prepending SOH to it if
    # it starts with NUL or SOH. This means that it can be converted to a number
    # and back, roundtripping correctly.
    s/^[␀␁]/␁$&/; #/# <- unconfuse Stack Exchange's syntax highlighting
    # Convert the input to a bignum, then to digits in base p$s, to split it
    # into blocks.
    @l=map{mod($_,p$s)}l(Math::BigInt->from_bytes($_),p$s);
    # Encoding can reuse code from decoding; we append $r "garbled blocks" to
    # the blocks representing the input, and run the decoder, to figure out what
    # values they should have.
    $#l+=$r;
    # Our degarbling algorithm can only handle at most p$s blocks in total. If
    # that isn't the case, try a higher $r (which will cause a huge increase in
    # $b and a reduction in @l).
    @l+$r>p($s)&&return e($_,$s);
    # Convert each block to a sequence of $s digits in base 128, adding 128 to
    # alternating blocks; this way, deleting up to $r (i.e. less than $s) bytes
    # will preserve the boundaries between each block; then convert that to a
    # string
    $a=0; # we must initialize $a to make this function deterministic
    join'',map{map{chr$_+$a}l($_->residue,128,$s,($a^=128))}g(@l)
}

sub d{ # decoder: arguments; encdng (bytestring)
    # Reconstruct the original blocks by looking at their top bits
    @l=split/([␀-␡]+)/,$_[0];
    @l||return''; # special case for empty string
    # The length of the longest block is the radiation parameter plus 1 (i.e.
    # $s). Use that to reconstruct the value of $s.
    $s=vecmax map length,@l;
    # Convert each block to a number, or to undef if it has the wrong length.
    # Then work out the values for the undefs.
    @l=g map{
        # Convert blocks with the wrong length to undef.
        length==$s&&
            # Convert other blocks to numbers, via removing any +128 and then
            # using Math::Polynomial to convert the digit list to a number.
            mod($m->new(map{ord()%128}split// #/# <- fix syntax highlighting
            )->evaluate(128),p$s)
    }@l;
    # Remove the redundant elements at the end; now that they've reconstructed
    # the garbled elements they have no further use.
    $#l-=$s-1;
    # Convert @l to a single number (reversing the conversion into blocks.)
    $_=$m->new(map{$_->residue}@l)->evaluate(p$s)
        # Convert that number into a string.
        ->to_bytes;
    # Delete a leading SOH.
    s/^␁//;  #/# <- unconfuse Stack Exchange's syntax highlighting
    # Finally, return the string.
    $_
}


### Testing code
use Encode qw/encode decode/;

# Express a string using control pictures + IBM437, to make binary strings
# easier for a human to parse
sub format_string {
    ($_)=@_;
    $_ = decode("Latin-1", $_);
    s/[\0-\x1f]/chr (0x2400 + ord $&)/eag;
    s/\x7f/chr 0x2421/eag;
    s/[ -~\x80-\xff]/decode("IBM437",$&)/eag;
    encode("UTF-8","\x{ff62}$_\x{ff63}")
}

sub test {
    my ($string, $radiation, $samples) = @_;
    say "Input: ", format_string($string);
    my $encoding = e($string, $radiation);
    say "Encoding: ", format_string($encoding);
    say "Input length ", length($string), ", encoding length ", length($encoding), ", radiation $radiation";
    my $decoding = d($encoding);
    $decoding eq $string or die "Mistake in output!";
    say "Decoding: ", format_string($decoding), " from ",
        format_string($encoding);

    # Pseudo-randomly generate $samples radiation-damaged versions.
    srand 1;
    for my $i (1..$samples) {
        my $encdng = $encoding;
        for my $r (1..$radiation) {
            substr $encdng, int(rand(length $encdng)), 1, "";
        }
        my $newdecoding = d($encdng);
        say "Decoding: ", format_string($newdecoding), " from ",
            format_string($encdng);
        $newdecoding eq $string or die "Mistake in output!";
    }

    say "";
    length $encoding;
}

test "abcdefghijklm", 1, 10;
test "abcdefghijklm", 2, 10;
test "abcdefghijklm", 5, 10;
test "abcdefghijklm", 10, 10;
test "\0\0\0\0\0", 1, 10;
test "\5\4\3\2\1", 2, 10;
test "a", 10, 10;

my %minlength = ();
my %maxlength = ();

for my $length (0..99) {
    my ($min, $max) = ("", "");
    $length and ($min, $max) =
        ("\2" . "\0" x ($length - 1), "\1" . "\377" x ($length - 1));
    for my $radiation (0..9) {
        $minlength{"$length-$radiation"} = test $min, $radiation, 1;
        $maxlength{"$length-$radiation"} = test $max, $radiation, 1;
    }
}

say "Minimum score: ", vecsum values %minlength;
say "Maximum score: ", vecsum values %maxlength;

ขั้นตอนวิธี

ลดความซับซ้อนของปัญหา

แนวคิดพื้นฐานคือการลดปัญหา "การเข้ารหัสการลบ" (ซึ่งไม่ใช่การสำรวจอย่างกว้างขวาง) ไปสู่ปัญหาการลบโค้ด (การสำรวจพื้นที่ทางคณิตศาสตร์อย่างครอบคลุม) แนวคิดเบื้องหลังการเข้ารหัสการลบคือคุณกำลังเตรียมข้อมูลที่จะส่งผ่าน "ช่องทางลบ" ซึ่งเป็นช่องที่บางครั้งแทนที่อักขระที่ส่งด้วยอักขระ "garble" ที่ระบุตำแหน่งที่ทราบข้อผิดพลาด (กล่าวอีกนัยหนึ่งก็ชัดเจนว่าเกิดการทุจริตแม้ว่าตัวละครดั้งเดิมยังไม่เป็นที่รู้จัก) ความคิดที่อยู่เบื้องหลังนั้นค่อนข้างง่าย: เราแบ่งการป้อนข้อมูลลงในบล็อกของความยาว ( รังสี+ 1) และใช้เจ็ดในแปดบิตในแต่ละบล็อกสำหรับข้อมูลในขณะที่บิตที่เหลือ (ในโครงสร้างนี้ MSB) จะสลับระหว่างการตั้งค่าสำหรับทั้งบล็อกชัดเจนสำหรับบล็อกถัดไปทั้งหมดตั้งสำหรับบล็อก หลังจากนั้นเป็นต้น เนื่องจากบล็อกมีความยาวมากกว่าพารามิเตอร์การแผ่รังสีอย่างน้อยหนึ่งอักขระจากแต่ละบล็อกจึงยังคงอยู่ในเอาต์พุต ดังนั้นด้วยการวิ่งของตัวละครที่มี MSB เดียวกันเราสามารถหาว่าบล็อกไหนเป็นตัวละครแต่ละตัว จำนวนบล็อกยังมากกว่าพารามิเตอร์การแผ่รังสีดังนั้นเราจึงมีบล็อกที่ไม่เสียหายอย่างน้อยหนึ่งครั้งใน encdng; เราจึงรู้ว่าบล็อคทั้งหมดที่ยาวที่สุดหรือถูกมัดไว้นานที่สุดนั้นไม่เสียหายทำให้เราสามารถรักษาบล็อคสั้น ๆ ที่เสียหายได้ (ซึ่งเป็น Garble) เราสามารถอนุมานพารามิเตอร์รังสีเช่นนี้ (มัน '

ลบการเข้ารหัส

ในส่วนของการลบโค้ดของปัญหาสิ่งนี้ใช้กรณีพิเศษแบบง่าย ๆ ของโครงสร้าง Reed-Solomon นี่คือการก่อสร้างอย่างเป็นระบบ: ผลลัพธ์ (อัลกอริธึมการเข้ารหัสลบข้อมูล) เท่ากับอินพุตบวกจำนวนบล็อกเพิ่มเติมเท่ากับพารามิเตอร์รังสี เราสามารถคำนวณค่าที่จำเป็นสำหรับบล็อกเหล่านี้ในวิธีที่ง่าย (และเล่นกอล์ฟ!) ผ่านการปฏิบัติต่อพวกมันในรูปแบบที่ยุ่งเหยิงจากนั้นรันอัลกอริธึมการถอดรหัสกับพวกมันเพื่อ

ความคิดที่แท้จริงที่อยู่เบื้องหลังการก่อสร้างนั้นง่ายมาก: เราพอดีกับพหุนามซึ่งเป็นระดับขั้นต่ำสุดที่เป็นไปได้กับบล็อกทั้งหมดในการเข้ารหัส ถ้าพหุนามเป็นfบล็อกแรกคือf (0) ส่วนที่สองคือf (1) และอื่น ๆ เป็นที่ชัดเจนว่าระดับของพหุนามจะเท่ากับจำนวนบล็อกของอินพุตลบ 1 (เพราะเราใส่พหุนามให้พอดีกับอันดับแรกจากนั้นใช้มันเพื่อสร้างบล็อค "เช็ค" พิเศษ) และเนื่องจากd 1 จุดที่ไม่ซ้ำกันกำหนดพหุนามของการศึกษาระดับปริญญาdการบิดเบือนจำนวนบล็อกใด ๆ (ขึ้นอยู่กับพารามิเตอร์การแผ่รังสี) จะทำให้จำนวนบล็อกที่ไม่เสียหายเท่ากับอินพุตดั้งเดิมซึ่งเป็นข้อมูลเพียงพอที่จะสร้างพหุนามเดียวกันขึ้นมาใหม่ (จากนั้นเราต้องประเมินพหุนามเพื่อคลายบล็อกหนึ่งบล็อก)

การแปลงฐาน

การพิจารณาขั้นสุดท้ายที่เหลืออยู่ที่นี่จะเกี่ยวกับมูลค่าที่แท้จริงของบล็อก ถ้าเราทำการแก้ไขพหุนามในจำนวนเต็มผลลัพธ์อาจเป็นจำนวนตรรกยะ (แทนที่จะเป็นจำนวนเต็ม) มีขนาดใหญ่กว่าค่าอินพุตหรือไม่พึงประสงค์ ดังนั้นแทนที่จะใช้จำนวนเต็มเราใช้สนาม จำกัด ในโปรแกรมนี้ฟิลด์ จำกัด ที่ใช้คือฟิลด์ของจำนวนเต็มโมดูโลpโดยที่pเป็นไพร์มที่ใหญ่ที่สุดที่น้อยกว่า 128 รังสี +1(เช่นนายกที่ใหญ่ที่สุดที่เราสามารถใส่จำนวนค่าที่แตกต่างกันเท่ากับนายกนั้นลงในส่วนข้อมูลของบล็อก) ข้อได้เปรียบที่สำคัญของฟิลด์ จำกัด คือการแบ่ง (ยกเว้นด้วย 0) ถูกกำหนดไว้อย่างไม่ซ้ำกันและจะสร้างมูลค่าภายในฟิลด์นั้นเสมอ ดังนั้นค่าที่ถูกแก้ไขของพหุนามจะพอดีกับบล็อกในลักษณะเดียวกับที่อินพุตทำ

ในการแปลงอินพุตเป็นชุดข้อมูลบล็อกจากนั้นเราต้องทำการแปลงฐาน: แปลงอินพุตจากฐาน 256 เป็นตัวเลขจากนั้นแปลงเป็นฐานp (เช่นสำหรับพารามิเตอร์รังสีที่ 1 เรามีp= 16381) นี่ส่วนใหญ่จัดขึ้นโดย Perl ของการขาดการแปลงขั้นพื้นฐาน (คณิตศาสตร์ :: Prime :: Util มีบางอย่าง แต่พวกเขาไม่ทำงานสำหรับฐาน bignum และบางช่วงเวลาที่เราทำงานด้วยที่นี่มีขนาดใหญ่อย่างไม่น่าเชื่อ) เนื่องจากเรากำลังใช้ Math :: Polynomial สำหรับการแก้ไขพหุนามอยู่แล้วฉันจึงสามารถนำมันกลับมาใช้ใหม่เป็นฟังก์ชั่น "แปลงจากตัวเลขหลัก" (ผ่านการดูตัวเลขเป็นค่าสัมประสิทธิ์ของพหุนามและประเมินผล) ได้ดี แม้ว่าจะไปทางอื่นฉันต้องเขียนฟังก์ชั่นด้วยตัวเอง โชคดีที่มันไม่ยากเกินกว่าที่จะเขียน น่าเสียดายที่การแปลงฐานนี้หมายความว่าโดยทั่วไปการแสดงผลจะไม่สามารถอ่านได้ นอกจากนี้ยังมีปัญหากับศูนย์นำ

ควรสังเกตว่าเราไม่สามารถมีบล็อกมากกว่าpบล็อกในเอาต์พุต (มิฉะนั้นดัชนีของบล็อกสองบล็อกจะเท่ากันและอาจจำเป็นต้องสร้างผลลัพธ์ที่แตกต่างจากพหุนาม) สิ่งนี้จะเกิดขึ้นเมื่ออินพุตมีขนาดใหญ่มากเท่านั้น โปรแกรมนี้จะช่วยแก้ปัญหาในวิธีที่ง่ายมาก: การเพิ่มการฉายรังสี (ซึ่งจะทำให้บล็อกขนาดใหญ่และหน้ามีขนาดใหญ่มากหมายถึงเราสามารถใส่ข้อมูลที่มากขึ้นในการและที่ชัดเจนนำไปสู่ผลที่ถูกต้อง)

อีกจุดหนึ่งที่น่าทำคือเราเข้ารหัสสตริง null ให้ตัวเองเพราะโปรแกรมที่เขียนจะขัดข้องเป็นอย่างอื่น เป็นการเข้ารหัสที่ดีที่สุดอย่างชัดเจนและไม่ว่าพารามิเตอร์การแผ่รังสีจะเป็นอย่างไร

การปรับปรุงที่มีศักยภาพ

ความไร้ประสิทธิภาพหลักของซีมโทติคในโปรแกรมนี้คือการใช้โมดูโล - ไพรม์เป็นฟิลด์ จำกัด ในคำถาม มีขอบเขตเขตข้อมูลขนาด 2 nอยู่ (ซึ่งเป็นสิ่งที่เราต้องการตรงนี้เพราะขนาดของส่วนบรรจุของบล็อกนั้นเป็นพลังของ 128) น่าเสียดายที่มันค่อนข้างซับซ้อนกว่าการสร้างโมดูโลแบบธรรมดาซึ่งหมายความว่า Math :: ModInt จะไม่ตัดมัน (และฉันไม่สามารถหาห้องสมุดใด ๆ ใน CPAN สำหรับการจัดการฟิลด์ที่มีขนาดไม่ใหญ่มาก) ฉันต้องเขียนทั้งคลาสด้วยคณิตศาสตร์ที่มากเกินไปสำหรับ Math :: Polynomial เพื่อให้สามารถจัดการกับมันได้และ ณ จุดนั้นค่าใช้จ่ายไบต์อาจสูงเกินกว่าการสูญเสีย (น้อยมาก) จากการใช้เช่น 16381 มากกว่า 16384

ข้อดีอีกอย่างของการใช้ขนาด power-of-2 ก็คือการแปลงฐานจะง่ายขึ้นมาก อย่างไรก็ตามในทั้งสองกรณีวิธีที่ดีกว่าในการแสดงความยาวของอินพุตจะมีประโยชน์ วิธี "การเพิ่ม 1 ในกรณีที่ไม่ชัดเจน" นั้นง่าย แต่สิ้นเปลือง การแปลงฐาน Bijective เป็นหนึ่งในวิธีที่เป็นไปได้ที่นี่ (แนวคิดคือคุณมีฐานเป็นตัวเลขและ 0 ไม่ใช่ตัวเลขดังนั้นแต่ละหมายเลขจะสอดคล้องกับสตริงเดียว)

แม้ว่าประสิทธิภาพของการเข้ารหัสของ asymptotic นี้จะดีมาก (เช่นสำหรับอินพุตของความยาว 99 และพารามิเตอร์การแผ่รังสีของ 3, การเข้ารหัสนั้นมีความยาว 128 ไบต์เสมอ, แทนที่จะเป็น ~ 400 ไบต์ที่ใช้วิธีการทำซ้ำ ๆ ) ไม่ดีในการป้อนข้อมูลสั้น ๆ ความยาวของการเข้ารหัสเป็นอย่างน้อยกำลังสองของพารามิเตอร์การแผ่รังสี + 1 ดังนั้นสำหรับอินพุตที่สั้นมาก (ความยาว 1 ถึง 8) ที่รังสี 9 ความยาวของเอาต์พุตคือ 100 อย่างไรก็ตาม (ที่ความยาว 9 ความยาวของเอาต์พุตเป็นบางครั้ง 100 และบางครั้ง 110. ) วิธีการที่ใช้การทำซ้ำอย่างชัดเจนเอาชนะการลบนี้ - วิธีการเข้ารหัสตามปัจจัยการผลิตที่มีขนาดเล็กมาก; มันอาจจะคุ้มค่าที่จะเปลี่ยนระหว่างอัลกอริทึมหลายตัวตามขนาดของอินพุต

ในที่สุดมันไม่ได้เกิดขึ้นจริงในการให้คะแนน แต่มีพารามิเตอร์การแผ่รังสีที่สูงมากการใช้บิตของทุกไบต์ (⅛ของขนาดเอาต์พุต) เพื่อกำหนดขอบเขตบล็อกนั้นสิ้นเปลือง มันจะถูกกว่าหากใช้ตัวคั่นระหว่างบล็อกแทน การสร้างบล็อคใหม่จากตัวคั่นค่อนข้างยากกว่าด้วยวิธีการสลับ MSB แต่ฉันเชื่อว่ามันเป็นไปได้อย่างน้อยถ้าข้อมูลมีความยาวเพียงพอ (ด้วยข้อมูลสั้น ๆ มันอาจยากที่จะอนุมานพารามิเตอร์รังสีจากเอาต์พุต) . นั่นจะเป็นสิ่งที่ต้องพิจารณาหากมีการเล็งหาแนวทางในอุดมคติแบบไม่ต้องคำนึงถึงพารามิเตอร์โดยไม่คำนึงถึงพารามิเตอร์

(และแน่นอนอาจมีอัลกอริทึมที่แตกต่างอย่างสิ้นเชิงที่ให้ผลลัพธ์ที่ดีกว่าอันนี้!)

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