สุ่มกอล์ฟประจำวัน # 6: สะสม d20


17

เกี่ยวกับซีรี่ส์

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

แม้ว่าฉันจะมีไอเดียมากมายสำหรับซีรีส์ แต่ความท้าทายในอนาคตยังไม่เกิดขึ้น หากคุณมีข้อเสนอแนะใด ๆ โปรดแจ้งให้เราทราบเกี่ยวกับการโพสต์ Sandbox ที่เกี่ยวข้อง

หลุม 6: หมุน d20

การตายที่พบบ่อยมากในเกม RPG สวมบทบาทบนโต๊ะคือการตายแบบยี่สิบหน้า ( icosahedronหรือที่รู้จักกันทั่วไปว่าd20 ) มันเป็นหน้าที่ของคุณที่จะต้องตายแบบนี้ อย่างไรก็ตามหากคุณเพิ่งส่งคืนหมายเลขสุ่มระหว่าง 1 ถึง 20 นั่นอาจเป็นเรื่องเล็กน้อย ดังนั้นงานของคุณคือการสร้างเครือข่ายสุ่มสำหรับการตายที่กำหนด

เราจะใช้เน็ตต่อไปนี้:

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

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

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

นั่นจะสอดคล้องกับตายต่อไปนี้ (ความจริงที่น่าสนุก: นี่คือตาข่ายที่ใช้โดยเวทย์มนตร์: ตัวรวบรวมชีวิต / ลูกเต๋าหมุนลง)

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

อย่างไรก็ตามนี่ไม่ใช่ตาข่ายเดียวที่แสดงถึงการตายนี้ เรามีตาข่ายแตกต่างกัน 60 ตัวทั้งนี้ขึ้นอยู่กับวิธีที่เราทำการเปิดหน้า นี่คืออีกสอง:

[1, 8, 9, 10, 2, 3, 4, 5, 6, 7, 17, 18, 19, 11, 12, 13, 14, 15, 16, 20]
[10, 9, 18, 19, 11, 12, 3, 2, 1, 8, 7, 17, 16, 20, 13, 14, 4, 5, 6, 15]

หรือกราฟิก (ฉันไม่ได้หมุนป้ายกำกับใบหน้าเพื่อความเรียบง่าย):

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

ความท้าทาย

ให้รายการของจำนวนเต็มแทนตาย (ตามที่อธิบายไว้ข้างต้น) และจำนวนเต็มN, เอาท์พุทNอิสระ, สุ่ม d20 ตาข่ายที่สอดคล้องกับตายที่กำหนด (นั่นคือตาข่ายที่เป็นไปได้ทั้ง 60 แห่งควรมีความน่าจะเป็นเหมือนกันในการสร้าง)

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

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

ด้วยข้อสันนิษฐานเหล่านี้อัลกอริทึมของคุณจะต้องให้การกระจายอย่างสม่ำเสมอ

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

คุณสามารถเขียนโปรแกรมหรือฟังก์ชั่น, รับอินพุตผ่าน STDIN (หรือทางเลือกที่ใกล้เคียงที่สุด), อาร์กิวเมนต์บรรทัดคำสั่งหรืออาร์กิวเมนต์ของฟังก์ชันและส่งผลลัพธ์ผ่าน STDOUT (หรือทางเลือกที่ใกล้เคียงที่สุด), ค่าส่งคืนของฟังก์ชันหรือพารามิเตอร์

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

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

ตัวอย่างผลลัพธ์

สำหรับการป้อนข้อมูล

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

60 ตาข่ายที่เป็นไปได้ (หากฉันไม่ได้ทำผิด) โดยไม่คำนึงถึงลำดับคือ:

[11, 10, 9, 18, 19, 20, 13, 12, 3, 2, 1, 8, 7, 17, 16, 15, 14, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
[8, 7, 17, 18, 9, 10, 2, 1, 5, 6, 15, 16, 20, 19, 11, 12, 3, 4, 14, 13]
[3, 12, 13, 14, 4, 5, 1, 2, 10, 11, 19, 20, 16, 15, 6, 7, 8, 9, 18, 17]
[3, 4, 5, 1, 2, 10, 11, 12, 13, 14, 15, 6, 7, 8, 9, 18, 19, 20, 16, 17]
[11, 19, 20, 13, 12, 3, 2, 10, 9, 18, 17, 16, 15, 14, 4, 5, 1, 8, 7, 6]
[4, 14, 15, 6, 5, 1, 2, 3, 12, 13, 20, 16, 17, 7, 8, 9, 10, 11, 19, 18]
[2, 10, 11, 12, 3, 4, 5, 1, 8, 9, 18, 19, 20, 13, 14, 15, 6, 7, 17, 16]
[4, 5, 1, 2, 3, 12, 13, 14, 15, 6, 7, 8, 9, 10, 11, 19, 20, 16, 17, 18]
[10, 2, 1, 8, 9, 18, 19, 11, 12, 3, 4, 5, 6, 7, 17, 16, 20, 13, 14, 15]
[3, 2, 10, 11, 12, 13, 14, 4, 5, 1, 8, 9, 18, 19, 20, 16, 15, 6, 7, 17]
[7, 8, 1, 5, 6, 15, 16, 17, 18, 9, 10, 2, 3, 4, 14, 13, 20, 19, 11, 12]
[13, 12, 11, 19, 20, 16, 15, 14, 4, 3, 2, 10, 9, 18, 17, 7, 6, 5, 1, 8]
[16, 15, 14, 13, 20, 19, 18, 17, 7, 6, 5, 4, 3, 12, 11, 10, 9, 8, 1, 2]
[15, 16, 17, 7, 6, 5, 4, 14, 13, 20, 19, 18, 9, 8, 1, 2, 3, 12, 11, 10]
[20, 13, 12, 11, 19, 18, 17, 16, 15, 14, 4, 3, 2, 10, 9, 8, 7, 6, 5, 1]
[5, 4, 14, 15, 6, 7, 8, 1, 2, 3, 12, 13, 20, 16, 17, 18, 9, 10, 11, 19]
[10, 11, 12, 3, 2, 1, 8, 9, 18, 19, 20, 13, 14, 4, 5, 6, 7, 17, 16, 15]
[4, 3, 12, 13, 14, 15, 6, 5, 1, 2, 10, 11, 19, 20, 16, 17, 7, 8, 9, 18]
[19, 20, 13, 12, 11, 10, 9, 18, 17, 16, 15, 14, 4, 3, 2, 1, 8, 7, 6, 5]
[1, 8, 9, 10, 2, 3, 4, 5, 6, 7, 17, 18, 19, 11, 12, 13, 14, 15, 16, 20]
[8, 1, 5, 6, 7, 17, 18, 9, 10, 2, 3, 4, 14, 15, 16, 20, 19, 11, 12, 13]
[18, 9, 8, 7, 17, 16, 20, 19, 11, 10, 2, 1, 5, 6, 15, 14, 13, 12, 3, 4]
[12, 3, 2, 10, 11, 19, 20, 13, 14, 4, 5, 1, 8, 9, 18, 17, 16, 15, 6, 7]
[2, 3, 4, 5, 1, 8, 9, 10, 11, 12, 13, 14, 15, 6, 7, 17, 18, 19, 20, 16]
[10, 9, 18, 19, 11, 12, 3, 2, 1, 8, 7, 17, 16, 20, 13, 14, 4, 5, 6, 15]
[9, 8, 7, 17, 18, 19, 11, 10, 2, 1, 5, 6, 15, 16, 20, 13, 12, 3, 4, 14]
[16, 17, 7, 6, 15, 14, 13, 20, 19, 18, 9, 8, 1, 5, 4, 3, 12, 11, 10, 2]
[17, 7, 6, 15, 16, 20, 19, 18, 9, 8, 1, 5, 4, 14, 13, 12, 11, 10, 2, 3]
[1, 5, 6, 7, 8, 9, 10, 2, 3, 4, 14, 15, 16, 17, 18, 19, 11, 12, 13, 20]
[9, 18, 19, 11, 10, 2, 1, 8, 7, 17, 16, 20, 13, 12, 3, 4, 5, 6, 15, 14]
[16, 20, 19, 18, 17, 7, 6, 15, 14, 13, 12, 11, 10, 9, 8, 1, 5, 4, 3, 2]
[5, 1, 2, 3, 4, 14, 15, 6, 7, 8, 9, 10, 11, 12, 13, 20, 16, 17, 18, 19]
[8, 9, 10, 2, 1, 5, 6, 7, 17, 18, 19, 11, 12, 3, 4, 14, 15, 16, 20, 13]
[13, 20, 16, 15, 14, 4, 3, 12, 11, 19, 18, 17, 7, 6, 5, 1, 2, 10, 9, 8]
[6, 15, 16, 17, 7, 8, 1, 5, 4, 14, 13, 20, 19, 18, 9, 10, 2, 3, 12, 11]
[6, 5, 4, 14, 15, 16, 17, 7, 8, 1, 2, 3, 12, 13, 20, 19, 18, 9, 10, 11]
[7, 6, 15, 16, 17, 18, 9, 8, 1, 5, 4, 14, 13, 20, 19, 11, 10, 2, 3, 12]
[19, 18, 17, 16, 20, 13, 12, 11, 10, 9, 8, 7, 6, 15, 14, 4, 3, 2, 1, 5]
[14, 15, 6, 5, 4, 3, 12, 13, 20, 16, 17, 7, 8, 1, 2, 10, 11, 19, 18, 9]
[17, 18, 9, 8, 7, 6, 15, 16, 20, 19, 11, 10, 2, 1, 5, 4, 14, 13, 12, 3]
[6, 7, 8, 1, 5, 4, 14, 15, 16, 17, 18, 9, 10, 2, 3, 12, 13, 20, 19, 11]
[14, 13, 20, 16, 15, 6, 5, 4, 3, 12, 11, 19, 18, 17, 7, 8, 1, 2, 10, 9]
[20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[7, 17, 18, 9, 8, 1, 5, 6, 15, 16, 20, 19, 11, 10, 2, 3, 4, 14, 13, 12]
[15, 6, 5, 4, 14, 13, 20, 16, 17, 7, 8, 1, 2, 3, 12, 11, 19, 18, 9, 10]
[9, 10, 2, 1, 8, 7, 17, 18, 19, 11, 12, 3, 4, 5, 6, 15, 16, 20, 13, 14]
[2, 1, 8, 9, 10, 11, 12, 3, 4, 5, 6, 7, 17, 18, 19, 20, 13, 14, 15, 16]
[12, 13, 14, 4, 3, 2, 10, 11, 19, 20, 16, 15, 6, 5, 1, 8, 9, 18, 17, 7]
[17, 16, 20, 19, 18, 9, 8, 7, 6, 15, 14, 13, 12, 11, 10, 2, 1, 5, 4, 3]
[18, 17, 16, 20, 19, 11, 10, 9, 8, 7, 6, 15, 14, 13, 12, 3, 2, 1, 5, 4]
[18, 19, 11, 10, 9, 8, 7, 17, 16, 20, 13, 12, 3, 2, 1, 5, 6, 15, 14, 4]
[11, 12, 3, 2, 10, 9, 18, 19, 20, 13, 14, 4, 5, 1, 8, 7, 17, 16, 15, 6]
[15, 14, 13, 20, 16, 17, 7, 6, 5, 4, 3, 12, 11, 19, 18, 9, 8, 1, 2, 10]
[19, 11, 10, 9, 18, 17, 16, 20, 13, 12, 3, 2, 1, 8, 7, 6, 15, 14, 4, 5]
[12, 11, 19, 20, 13, 14, 4, 3, 2, 10, 9, 18, 17, 16, 15, 6, 5, 1, 8, 7]
[20, 16, 15, 14, 13, 12, 11, 19, 18, 17, 7, 6, 5, 4, 3, 2, 10, 9, 8, 1]
[13, 14, 4, 3, 12, 11, 19, 20, 16, 15, 6, 5, 1, 2, 10, 9, 18, 17, 7, 8]
[5, 6, 7, 8, 1, 2, 3, 4, 14, 15, 16, 17, 18, 9, 10, 11, 12, 13, 20, 19]
[14, 4, 3, 12, 13, 20, 16, 15, 6, 5, 1, 2, 10, 11, 19, 18, 17, 7, 8, 9]

สำหรับเน็ตอื่น ๆ เพียงแทนที่ทุกเหตุการณ์ที่เกิดขึ้นiด้วยiหมายเลข th ในอินพุต (โดยใช้i1-based)

ความท้าทายที่เกี่ยวข้อง

ลีดเดอร์บอร์ด

โพสต์แรกของซีรีส์สร้างกระดานผู้นำ

เพื่อให้แน่ใจว่าคำตอบของคุณปรากฏขึ้นโปรดเริ่มต้นทุกคำตอบด้วยบรรทัดแรกโดยใช้เทมเพลต Markdown ต่อไปนี้:

## Language Name, N bytes

ที่Nมีขนาดของส่งของคุณ หากคุณปรับปรุงคะแนนของคุณคุณสามารถเก็บคะแนนเก่าไว้ในพาดหัวโดยการตีพวกเขาผ่าน ตัวอย่างเช่น

## Ruby, <s>104</s> <s>101</s> 96 bytes

(ภาษาไม่ปรากฏขึ้นในปัจจุบัน แต่ตัวอย่างข้อมูลต้องการและแยกวิเคราะห์และฉันอาจเพิ่มกระดานผู้นำแบบแยกภาษาในอนาคต)


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

นี่เกือบจะเหมือนกับการโพสต์แซนด์บ็อกซ์ของฉัน!
Rɪᴋᴇʀ

@RikerW นั่นคือสิ่งที่ฉันคิดว่าเมื่อคุณ sandboxed มัน ;) (ในขณะนั้นฉันอยู่ใต้คุณโดยตรงฉันคิดว่าอันนี้เป็นแรงบันดาลใจให้คุณ) เห็นได้ชัดว่าคุณง่ายกว่ามาก (ซึ่งอาจเป็นสิ่งที่ดี)
Martin Ender

ไม่เคยเห็นคุณ แปลกมากฉันคิดว่าฉันอ่านทั้งหมดในหน้าแรก แต่ไม่ฉันทำของฉันเอง
Rɪᴋᴇʀ

ไม่ควรตั้งชื่อเป็น "แฉ d20" หรือไม่?
ติตัส

คำตอบ:


7

Ruby, 160 ไบต์ (รอบ B)

บันทึกแล้ว 17 ไบต์ด้วยคำแนะนำจาก Martin Büttner

->a,n{n.times{k=rand 60
%w{ABCD@GHIJKLMNEFPQRSO PFENOSRQHG@DCMLKJIAB GFPQHIA@DENOSRJKBCML}.map{|b|k.times{a=b.chars.map{|i|a[i.ord-64]}}}
k>29&&a.reverse!
p a}}

Ruby, 177 ไบต์ (rev A)

->n,a{n.times{k=rand(60)
h=->b{k.times{|j|a=(0..19).map{|i|a[b[i].ord-64]}}}
h['ABCD@GHIJKLMNEFPQRSO']
h['PFENOSRQHG@DCMLKJIAB']
h['GFPQHIA@DENOSRJKBCML']
k>29&&a.reverse!
p a}}

คำอธิบาย

มันเป็นไปได้ที่จะสร้างทิศทางที่เป็นไปได้ทั้งหมดโดยการหมุนประมาณหนึ่งใบหน้า (3-fold), หนึ่งจุดยอด (5-fold) และสองขอบ (2-fold) แต่ไม่ใช่แค่ใบหน้าและขอบ แกนจะต้องมีความสัมพันธ์ที่ถูกต้องและการหมุนจะต้องทำในลำดับที่ถูกต้องหรือสิ่งที่แปลกสามารถเกิดขึ้นได้

นี่คือวิธีที่ฉันทำ (แม้ว่าฉันจะเข้าใจว่ามาร์ตินทำมันแตกต่างกัน)

การวางแนวจัตุรมุขทั้งหมดสามารถสร้างขึ้นได้ด้วยการผสมผสานของการปฏิบัติการสมมาตรสามแบบต่อไปนี้:

a) การหมุนแกน 2-fold สองอันทางด้านขวาผ่านจุดกึ่งกลางของขอบ (นี่คือมุมฉากซึ่งกันและกันถ้าเราคูณพวกมันเข้าด้วยกันเราจะพบแกน 2-fold ที่สามผ่านคู่ของขอบที่เหลือ)

b) การหมุนประมาณ 3 แกนในแนวทแยงไปยังแกน 2 เท่าผ่านจุดยอดและใบหน้า

ความสมมาตรของ icosahedron นั้นเป็นสิ่งที่เกินกว่าของจัตุรมุข ในภาพด้านล่างจากhttps://en.wikipedia.org/wiki/Icosahedronใบหน้าสีเหลืองและใบหน้าสีแดงจะกำหนด Tetraedra สองรูปแบบที่แตกต่างกัน (หรือรูปแปดด้านหนึ่งรูปแปดเหลี่ยม) ในขณะที่ขอบที่มีสีฟ้าสองใบหน้าอยู่ในสามคู่ที่ มุมฉาก (และนอนคว่ำหน้าเป็นลูกบาศก์)

การวางแนวของ icosahedron ทั้งหมดสามารถสร้างขึ้นได้จากการดำเนินการสมมาตรที่กล่าวถึงข้างต้นรวมถึงการเพิ่มอีก 5 เท่า

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

การหมุนประมาณสามในสี่แกนดังกล่าวข้างต้นนั้นจะถูกแสดงด้วยสายเวทระหว่าง''เครื่องหมาย การหมุนรอบแกน 2 a[]เท่าที่สองคือการที่แตกต่างกันและสามารถทำได้เพียงแค่การย้อนกลับของอาร์เรย์

Ungolfed ในโปรแกรมทดสอบ:

f=->a,n{
  n.times{                     #Iterate n times, using the result from the previous iteration to generate the next
    k=rand(60)                 #pick a random number

    h=->b{                     #helper function taking a string representing a transformation
      k.times{|j|              #which is performed on a using the number of times according to k
        a=(0..19).map{|i|a[b[i].ord-64]}
      }
    }

    #Rotate about axes k times (one 5-fold, one 3-fold, two 2-fold)
    #The first three axes have coprime rotation orders
    #And the rotations themselves take care of the modulus operation so no need to add it.
    #The second 2-fold rotation is equivalent to reversing the order
    #And is applied to the last 30 numbers as it is not coprime with the first 2-fold rotation.

    h['ABCD@GHIJKLMNEFPQRSO']  #rotate k times about 5-fold axis
    h['PFENOSRQHG@DCMLKJIAB']  #rotate k times about 3-fold axis
    h['GFPQHIA@DENOSRJKBCML']  #rotate k times about 2-fold axis
    k>29&&a.reverse!
    p a
  }
}

z=(1..20).map{|i|i} 
f[z,50]

ทางเลือกโซลูชัน 131 ไบต์ (ไม่ถูกต้องเนื่องจากวิธีการสุ่มเดินแบบไบนารีให้การกระจายที่ถูกต้องโดยประมาณเท่านั้น)

->a,n{(n*99).times{|i|s=['@DEFGHIABCMNOPQRJKLS','ABCD@GHIJKLMNEFPQRSO'][rand(2)] 
a=(0..19).map{|i|a[s[i].ord-64]}
i%99==98&&p(a)}}

นี่เป็นช่วงชิง (เหมือนโปรแกรมที่ใช้ในการแย่งลูกบาศก์ของรูบิค)

การหมุนเฉพาะที่ฉันใช้นั้นเป็นสองสิ่งที่ชัดเจนที่สุด:

-A การหมุน 120 องศา (ประมาณใบหน้า 1 และ 20 ต่อไดอะแกรมแรก)

-A การหมุน 72 องศา (เกี่ยวกับจุดยอดทั่วไปที่ 1,2,3,4,5 และ 16,17,18,19,20 ต่อแผนภาพแรก)

เราพลิกเหรียญ 99 ครั้งและแต่ละครั้งที่เราทำการหมุนหนึ่งในสองอันนี้ขึ้นอยู่กับว่ามันเป็นหัวหรือก้อย

โปรดทราบว่าการสลับลำดับค่าเหล่านี้จะนำไปสู่ลำดับที่ค่อนข้างสั้น ตัวอย่างเช่นด้วยความรู้สึกการหมุนที่ถูกต้องสามารถหมุนได้ 180 องศาด้วยระยะเวลาเพียง 2


ดูเหมือนว่าการโยนเหรียญเพื่อเลือกการดำเนินการจะให้บางสิ่งที่ใกล้เคียงกับการแจกแจงแบบทวินามมากกว่าการกระจายแบบสม่ำเสมอ
Sparr

@ Sparr จะเป็นกรณีนี้หากพื้นที่ของรัฐมีขนาดใหญ่กว่าการเดินสุ่ม แต่ในกรณีนี้การเดินแบบสุ่มเพียง 6 ขั้นอาจเปิดได้มากถึง 2 ^ 6 = 64 ความเป็นไปได้ (ฉันไม่ได้นับพวกเขา) และพื้นที่รัฐของเรามีเพียง 60 หลังจาก 99 ขั้นตอน (2 ^ 99 เส้นทางที่แตกต่างกัน) ทุกอย่างควรมีอย่างน้อยที่สุดเท่าการกระจายอย่างเท่าเทียมกันเป็นตัวอย่างเดียวของ PRNG ที่ใช้ในการสร้างตัวเลข
เลเวลริเวอร์เซนต์

@ MartinBüttnerขอบคุณสำหรับเคล็ดลับฉันได้ใช้สิ่งที่ใช้ได้ b.mapไม่ทำงานโดยตรงฉันต้องb.chars.mapทำให้มันใช้งานได้ (BTW ที่ไม่ทำงานในเครื่องของฉันเนื่องจากฉันมี Ruby 1.9.3 แต่ใช้งานได้กับ Ideone) มันประหยัดอย่างเป็นธรรม ฉันไม่คิดว่าการเปลี่ยนสายเวทย์มนตร์สำหรับตัวละครที่ไม่สามารถพิมพ์ได้เพื่อบันทึก-64จะทำงาน: %w{}ตีความ\n(เช่นเดียวกับพื้นที่) เป็นตัวคั่นระหว่างสตริงในอาร์เรย์ที่สร้าง ฉันไม่รู้ว่ามันจะทำอย่างไรกับตัวละคร ASCII ที่ไม่สามารถพิมพ์ได้อื่น ๆ
เลเวลริเวอร์เซนต์

@ มาร์ตินนี่ยากกว่าที่ฉันคาดไว้ - ตอนแรกฉันงงงวยเมื่อรหัสของฉันไม่ทำงานอย่างถูกต้องจากนั้นฉันก็หยุดพักและก็รู้ทันทีว่า symmetries 2 เท่าและ 3 เท่าต้องมีความสัมพันธ์ร่วมกันเช่นเดียวกับ บนจัตุรมุข (ฉันต้องเปลี่ยนหน้ารูปสามเหลี่ยมซึ่งฉันหมุนไปรอบ ๆ สำหรับหน้ารูปสามเหลี่ยมที่แตกต่างกัน)
เลเวลริเวอร์เซนต์

1
ยินดีด้วยที่เป็นผู้ใช้รายแรกที่มีตราสัญลักษณ์รูปทรงเรขาคณิตที่เพิ่งปลดล็อก :)
Martin Ender

2

รหัสเครื่อง IA-32 ขนาด 118 ไบต์

hexdump:

60 33 c0 51 8b 74 24 28 8b fa 6a 05 59 f3 a5 e8
21 00 00 00 20 c4 61 cd 6a 33 00 84 80 ad a8 33
32 00 46 20 44 8e 48 61 2d 2c 33 32 4a 00 21 20
a7 a2 90 8c 00 5b b1 04 51 0f c7 f1 83 e1 1f 49
7e f7 51 8b f2 56 8d 7c 24 e0 b1 14 f3 a4 5f 8b
f3 ac 8b ee d4 20 86 cc e3 0a 56 8d 74 04 e0 f3
a4 5e eb ed 59 e2 db 8b dd 59 e2 cc 59 83 c2 14
e2 91 61 c2 04 00

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

รหัสโดยทั่วไปจะต้องสร้างการเปลี่ยนแปลงซึ่งเป็นโปรแกรมต่อไปนี้:

  1. เรียงสับเปลี่ยนของคำสั่ง 3 ซึ่งฉันเรียก p3ใช้นั้นใช้ 0 ... 2 ครั้ง
  2. เรียงสับเปลี่ยนของคำสั่งที่ 2 ซึ่งฉันเรียกว่า q2ใช้มีค่า 0 หรือ 1 ครั้ง
  3. เรียงสับเปลี่ยนของคำสั่ง 5 ซึ่งฉันเรียก p5ใช้นั้นใช้ 0 ... 4 ครั้ง
  4. การเรียงสับเปลี่ยนของคำสั่งที่ 2 ซึ่งฉันเรียก p2ใช้กับ 0 หรือ 1 ครั้ง

มีทางเลือกมากมายสำหรับการเรียงสับเปลี่ยนเหล่านี้ หนึ่งในนั้นคือ:

p3 = [0   4   5   6   7   8   9   1   2   3  13  14  15  16  17  18  10  11  12  19]
q2 = [4   5   6   7   0   1   2   3  13  14  15  16  17   8   9  10  11  12  19  18]
p5 = [6   7   0   4   5  14  15  16  17   8   9   1   2   3  13  12  19  18  10  11]
p2 = [1   0   7   8   9  10  11   2   3   4   5   6  16  17  18  19  12  13  14  15]

ตัวเลือกนี้ดีกว่าตัวเลือกอื่นเนื่องจากการเรียงสับเปลี่ยนที่นี่มีดัชนีเรียงลำดับแบบยาวซึ่งสามารถบีบอัดได้ด้วยการเข้ารหัสความยาวรัน - มีเพียง 29 ไบต์สำหรับ 4 การเรียงสับเปลี่ยน

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

พลังควรเป็นบวกดังนั้นการดำเนินการถอดรหัสแบบรันไทม์จะกระทำอย่างน้อยหนึ่งครั้ง

รหัสนี้มีลูปซ้อนกัน 4 วง โครงร่างเป็นดังนี้:

void doit(int n, uint8_t* output, const uint8_t input[20])
{    
    uint8_t temp[20];

    n_loop: for i_n = 0 ... n
    {
        memcpy(output, input, 20);
        expr_loop: for i_expr = 0 ... 3
        {
            power = rand(1 ... 30);
            power_loop: for i_power = 0 ... power
            {
                memcpy(temp, output, 20);
                output_index = 0;
                perm_loop: do while length > 0
                {
                    index = ...; // decode it
                    length = ...; // decode it
                    memcpy(output + output_index, temp + index, length);
                    output_index += length;
                }
            }
        }
        output += 20;
    }
}

ฉันหวังว่าโค้ดหลอกนี้ชัดเจนกว่าโค้ดอินไลน์แอสเซมบลีด้านล่าง

_declspec(naked) void __fastcall doit(int n, uint8_t* output, const uint8_t* input)
{
    _asm {
        pushad
        xor eax, eax

        n_loop:
            push ecx

            ; copy from input to output
            mov esi, [esp + 0x28]
            mov edi, edx
            push 5
            pop ecx
            rep movsd

            call end_of_data
#define rl(index, length) _emit(length * 32 + index)
            rl(0, 1)
            rl(4, 6)
            rl(1, 3)
            rl(13, 6)
            rl(10, 3)
            rl(19, 1)
            _emit(0)

            rl(4, 4)
            rl(0, 4)
            rl(13, 5)
            rl(8, 5)
            rl(19, 1)
            rl(18, 1)
            _emit(0)

            rl(6, 2)
            rl(0, 1)
            rl(4, 2)
            rl(14, 4)
            rl(8, 2)
            rl(1, 3)
            rl(13, 1)
            rl(12, 1)
            rl(19, 1)
            rl(18, 1)
            rl(10, 2)
            _emit(0)

            rl(1, 1)
            rl(0, 1)
            rl(7, 5)
            rl(2, 5)
            rl(16, 4)
            rl(12, 4)
            _emit(0)

            end_of_data:
            pop ebx ; load the address of the encoded data
            mov cl, 4

            expr_loop:
                push ecx

                make_rand:
                rdrand ecx
                and ecx, 31
                dec ecx
                jle make_rand

                ; input: ebx => encoding of permutation
                ; output: ebp => encoding of next permutation
                power_loop:
                    push ecx

                    ; copy from output to temp
                    mov esi, edx
                    push esi
                    lea edi, [esp - 0x20]
                    mov cl, 20
                    rep movsb
                    pop edi

                    ; ebx => encoding of permutation
                    ; edi => output
                    mov esi, ebx
                    perm_loop:
                        ; read a run-length
                        lodsb
                        mov ebp, esi

                        _emit(0xd4)             ; divide by 32, that is, split into
                        _emit(32)               ; index (al) and length (ah)
                        xchg cl, ah             ; set ecx = length; also makes eax = al
                        jecxz perm_loop_done    ; zero length => done decoding
                        push esi
                        lea esi, [esp + eax - 0x20]
                        rep movsb
                        pop esi
                        jmp perm_loop

                    perm_loop_done:
                    pop ecx
                    loop power_loop

                mov ebx, ebp
                pop ecx
                loop expr_loop

            pop ecx
            add edx, 20
            loop n_loop

        popad
        ret 4
    }
}

รายละเอียดการใช้งานที่สนุก:

  • ฉันใช้ชุดการเยื้องในภาษาระดับสูง มิฉะนั้นรหัสจะเป็นระเบียบที่เข้าใจไม่ได้
  • ฉันใช้callและตามมาpopในการเข้าถึงข้อมูล (การเรียงสับเปลี่ยน) ที่เก็บไว้ในรหัส
  • jecxzคำแนะนำและอำนวยความสะดวกให้ผมใช้ไบต์เป็นศูนย์การสิ้นสุดกระบวนการถอดรหัสวิ่งยาว
  • โชคดีหมายเลข 30 (2 * 3 * 5) เกือบจะเป็นพลัง 2 ซึ่งทำให้ฉันใช้รหัสสั้น ๆ เพื่อสร้างตัวเลขในช่วง 1 ... 30:

            and ecx, 31
            dec ecx
            jle make_rand
    
  • ฉันใช้คำสั่ง "การแบ่งวัตถุประสงค์ทั่วไป" ( aam) เพื่อแยกไบต์เป็นฟิลด์บิต (ความยาว 3 บิตและดัชนี 5 บิต); โดยโชคที่ตำแหน่งนั้นในรหัสcl = 0ดังนั้นฉันได้รับประโยชน์จาก "ทั้งสอง" ของxchg

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