sudoku ของ Dyalog APL จะคืนค่า เมทริกซ์คำตอบทั้งหมดที่เป็นไปได้ จากเมทริกซ์ปริศนาที่ใช้ 0 แทนช่องว่าง และมีการนำโจทย์เดียวกันไปใช้งานหลายรูปแบบในสไตล์ APL/K
- เป้าหมายหลักคือ Sudoku ขนาด 9×9 ซึ่งแต่ละ กล่อง 3×3, แถว และคอลัมน์ ต้องมีตัวเลข 1 ถึง 9 โดยไม่ซ้ำกัน
- อินพุต
prob ใส่ค่า 1-9 ในช่องที่เติมแล้ว และ 0 ในช่องว่าง และสามารถระบุ กล่องที่ไม่ใช่สี่เหลี่ยมจัตุรัส เช่น 2×3, 3×4 ได้ด้วยอาร์กิวเมนต์ซ้ายแบบเลือกได้ shape
- อัลกอริทึมการแก้ของ Veli-Matti Jantunen จะแปลงเมทริกซ์เป็นเวกเตอร์ สร้างดัชนีแถว·คอลัมน์·กล่อง แล้วค่อย ๆ ลดตัวเลือก พร้อมขยายจาก กลุ่มที่มีข้อจำกัดมากที่สุด ก่อน
- ตัวอย่าง
s33 และ s22 มีคำตอบอย่างละ 3 แบบ ส่วน 3 4 sudoku s34 มีคำตอบ 2 แบบ และยังแนะนำวันไลเนอร์ K 5 ของ Arthur Whitney พร้อมการนำไปใช้งานใหม่ใน APL หลายแบบ
อินพุต Sudoku และผลลัพธ์ของฟังก์ชัน sudoku
- ปริศนา Sudoku คือกริดที่จัดวางกล่อง 3×3 เป็นรูปแบบ 3×3 โดยแต่ละช่องอาจว่างหรือมีตัวเลขตั้งแต่ 1 ถึง 9
- คำตอบต้องเป็นไปตามเงื่อนไขห้ามซ้ำทั้งสามข้อ
- ในแต่ละ กล่อง 3×3 ต้องมีตัวเลข 1 ถึง 9 โดยไม่ซ้ำกัน
- ในแต่ละ แถว 9 ช่อง ต้องมีตัวเลข 1 ถึง 9 โดยไม่ซ้ำกัน
- ในแต่ละ คอลัมน์ 9 ช่อง ต้องมีตัวเลข 1 ถึง 9 โดยไม่ซ้ำกัน
- เมทริกซ์
prob ใช้ตัวเลข 1-9 สำหรับช่องที่เติมแล้ว และใช้ 0 สำหรับช่องว่าง
- อาร์กิวเมนต์ซ้ายแบบเลือกได้
shape ใช้ระบุ รูปทรงของกล่อง สำหรับปริศนาที่ไม่ใช่แบบสี่เหลี่ยมจัตุรัสมาตรฐาน
- หากเป็นเมทริกซ์ 6×6 ที่พื้นที่ย่อยเป็น 2×3 ให้เรียกในรูป
2 3 sudoku mat
- ผลลัพธ์เป็นเวกเตอร์ที่บรรจุเมทริกซ์คำตอบทั้งหมด
- หากไม่มีคำตอบ จะคืนค่า
⍬
- สถานการณ์ผิดพลาดอาจแสดงเป็น
'' โดยเอกสารระบุว่า “ไม่ควรเกิดขึ้น แต่เกิดเมื่อผลลัพธ์มีจำนวนมากมหาศาล”
ลำดับการทำงานของวิธีแก้ของ Veli-Matti Jantunen
- อัลกอริทึมจัดการเมทริกซ์ Sudoku เป็น เวกเตอร์ และแทนแถว·คอลัมน์·พื้นที่ Sudoku แต่ละแบบด้วย เวกเตอร์ดัชนี
- หลังผ่านการตรวจสอบพื้นฐานแล้ว จะตรวจทางเลือกในรายการตัวเลือกทีละรายการ
- ในแต่ละขั้นตอน จะกรองค่าที่เป็นไปได้ของทุกช่อง
- หากมีช่องใดช่องหนึ่งที่ไม่มีค่าที่เป็นไปได้เลย จะตัดออกจากตัวเลือกคำตอบ
- หากช่องหนึ่งมีตัวเลขผู้สมัครมากกว่าหนึ่งตัว จะเลือกช่องจากกลุ่มที่มีข้อจำกัดมากที่สุด แล้วเพิ่มชุดตัวเลือกของช่องนั้นลงในรายการ
- หากทุกช่องเหลือตัวเลขเพียงตัวเดียว จะถือเป็นคำตอบ แล้วไปยังตัวเลือกถัดไป
- ในส่วนเดียวกันยังมีฟังก์ชัน
Shuffle สำหรับสลับตาราง Sudoku เดิมให้เป็นตารางอื่นด้วย
วันไลเนอร์ของ Arthur Whitney และการใช้งานทางเลือก
- การใช้งาน
sudoku ทางเลือกของ David Crossley รับการตั้งค่า N×N เป็นอินพุต และรองรับกรณีที่ขนาดกล่อง N*÷2 เป็นจำนวนเต็ม
- อินพุตต้องเป็นการจัดวางที่ถูกต้อง โดยบางช่องมีตัวเลขตั้งแต่ 1 ถึง
N และช่องที่เหลือเป็น 0
- ในผลลัพธ์ แต่ละแถว คอลัมน์ และกล่อง ต้องมีตัวเลขตั้งแต่ 1 ถึง
N ครบทุกตัว
- ภายในการใช้งานมีฟังก์ชันช่วยอย่าง
valid, search, rules, sole, singles, uniques, matches, NinN, setup
- โซลูชัน K 5 ของ Arthur Whitney ถูกนำเสนอเป็น โค้ดหนึ่งบรรทัด
x(,/{@[x;y;]'(!10)^x*|/p[;y]=p,:,3/:-3!p:!9 9}')/&~* x
- Phil Last ให้การใช้งาน
sudoku ที่แปลงโค้ดของ Whitney ไปเป็น D-function
- การเขียนใหม่ของ Morten Kromberg นิยามองค์ประกอบบางส่วนของ K อย่างชัดเจน ทำให้มีรูปแบบใกล้กับต้นฉบับมากขึ้น
- เช่นเดียวกับเวอร์ชัน K อินพุตและเอาต์พุตไม่ใช่เมทริกซ์ แต่เป็น เวกเตอร์ 81 องค์ประกอบ
- การใช้งาน
Sudoku ของ Roger Hui เป็นรูปแบบที่ทั่วไปกว่า และรองรับปริศนาที่ไม่ใช่สี่เหลี่ยมจัตุรัสด้วย
svec สร้างเวกเตอร์คำตอบ ส่วน pvex และ pvec ขยายการจัดวางที่เป็นไปได้
avl สร้างรายการตัวเลขที่เป็นไปได้ และ emt ค้นหาดัชนีแถว·คอลัมน์ของช่องว่าง
rcb, box, cmap, CMAP สร้างความสัมพันธ์การชนกันของแถว·คอลัมน์·กล่อง
ตัวอย่างปริศนาและจำนวนคำตอบ
s33 เป็นโจทย์ตัวอย่าง 9×9 และผลลัพธ์ของ sudoku s33 มี คำตอบ 3 แบบ
- ฟังก์ชัน
sbox แบ่งกล่องภายในเพื่อแสดงกริด Sudoku ให้อ่านง่าย
- 0 จะแสดงเป็นจุด (
·)
- เอาต์พุตเป็นเมทริกซ์อักขระที่วาดเส้นขอบกล่องไว้
s22 เป็นโจทย์ตัวอย่าง 4×4 และผลลัพธ์ของ sbox¨ sudoku s22 มี คำตอบ 3 แบบ
s34 เป็นโจทย์ตัวอย่างที่ใช้กล่อง 3×4
- ใช้
3 4 sbox s34 เพื่อแสดงโจทย์ในรูปแบบแยกกล่อง
- ผลลัพธ์ของ
3 4 sudoku s34 มี คำตอบ 2 แบบ
ลิงก์อ้างอิงและรายการที่เกี่ยวข้อง
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
บรรทัดดังกล่าวเขียนด้วย K โดย K เป็นภาษาที่ Arthur Whitney สร้างขึ้นจากพื้นฐานของ APL และ Scheme
x(,/{@[x;y;]'(!10)^x*|/p[;y]=p,:,3/:-3!p:!9 9}')/&~*xบางครั้งก็ประเมินความซับซ้อนของโค้ดโดยเทียบจำนวนบรรทัดของโค้ดกับผลลัพธ์ด้านล่าง
tar -cf - . | gzip | base64 | wc -lกล่าวคือดูว่า “บีบอัดได้ดีแค่ไหน?” พอเห็น APL ก็ทำให้นึกถึงตอนที่เผลอส่งเอาต์พุตของ gzip ไปยังเทอร์มินัล
p←{(↑⍵)∘{(⍺∨.=⍵)/⍳n×n∘}¨,⍵},(n*÷2){⍵,⍺⊥⌊⍵÷⍺}'⍳n n←⍴⍵น่าประทับใจที่ยังมีคนไล่อ่านโค้ดแบบนี้แล้วถึงขั้นถามว่า “หา bug เจอไหม?” มันให้ความรู้สึกเหมือน ข้อมูลไบนารีที่ถูกบีบอัด ซึ่งทุกคนมีพจนานุกรมเดียวกันอยู่แล้ว
∘ที่ไม่มี operand ด้านขวา ส่วนn n←⍴⍵ดูเหมือนสัญญาณว่าตั้งค่าnซ้ำสองครั้งและคาดว่า⍵เป็นสองมิติ แต่ขึ้นอยู่กับเจตนาแล้ว_ n←⍴⍵หรือn←⊃⌽⍴⍵น่าจะเป็นธรรมชาติกว่าอีกอย่าง
⊥จะเกิด error ถ้า⍴⍵ไม่ใช่จำนวนเต็มเดี่ยวหรือเวกเตอร์ว่าง ดังนั้นสุดท้ายก็แทบไม่ต่างจากn←⍴⍵และยิ่งทำให้สับสนกว่าเดิม ส่วน,ที่ซ้ำกันหลายจุดกับ↑⍵ก็เอาออกได้ และทั้ง expression จริง ๆ แล้วแทบจะเท่ากับp←(n+1)⍴⊂⍳n×n←⍴⍵ซึ่งเป็นโครงสร้างที่คืนเวกเตอร์1..n²ออกมาn+1ชุดแม้ภายนอกจะดูแปลก แต่เมื่อเรียนรู้สัญลักษณ์และ operation พื้นฐานแล้ว APL กลับตรงไปตรงมาอย่างคาดไม่ถึง เพียงแต่ต้องใช้เวลาเพื่อให้ชำนาญ และเมื่อไปถึงจุดนั้นก็รู้สึกเหมือนมีพลังพิเศษ
ที่บอกว่าผู้สนับสนุนภาษานี้เน้นเรื่องความเร็ว ความง่ายในการประมวลผล array และ syntax ที่แสดงออกได้ดี นั้นถูกต้อง
https://en.m.wikipedia.org/wiki/K_(programming_language)
จำนวนบรรทัดของโค้ดเป็นตัวชี้วัดที่ไม่ดี เพราะแต่ละภาษามีวิธีเขียนบรรทัดต่างกัน
มาตรวัดที่ดีกว่าอาจเป็นการนับ จำนวนโหนดใน syntax tree ตาม non-terminal symbol ที่มีความหมาย เช่น “ค่าคงที่” หรือ “การเรียกฟังก์ชัน” และถ้าพิจารณาความลึกกับ branching factor ของต้นไม้นั้นด้วยก็ยิ่งดี
วิธีแก้แบบบรรทัดเดียวแทบไม่กินพื้นที่หน้าจอ จึงเป็นข้อได้เปรียบมากเมื่อต้องจัดการปัญหาซับซ้อน การขยับสายตาภายในหน้าจอเดียวมีภาระน้อยกว่าการเลื่อนและสลับไปมาระหว่างไฟล์มาก และ cognitive load ก็สำคัญ
ต่อให้ไม่รู้จัก K ถ้าเห็นค่าคงที่เรียงอยู่ข้างกัน ก็ดูเหมือนกำลังใช้การแทนข้อมูลของปัญหาโดยตรง ถ้าวัฒนธรรมของ K ส่งเสริมโค้ดแบบนี้และทำให้ความคิดเอนไปทางความตรงไปตรงมากับความเรียบง่าย ก็อยากเอา ซอสสูตรพิเศษ แบบนั้นเข้าทีม
https://cliffle.com/esoterica/hq9plus/
https://en.wikipedia.org/wiki/Algorithmic_information_theory
เคยสงสัยอยู่บ่อย ๆ ว่าการใช้ภาษาอย่าง APL/K ทำให้โปรแกรมเมอร์คิดแก้ปัญหาได้มีประสิทธิภาพขึ้นจริงหรือไม่
avg a+bเพื่อบวก array สองตัวแล้วหาค่าเฉลี่ยนั้นง่ายกว่าอย่างชัดเจนถ้าเป็นภาษาที่ไม่ได้มี array เป็นศูนย์กลาง ก็น่าจะต้องมีการตรวจขอบเขต loop
forขนาดใหญ่ ตัวแปรชั่วคราวสำหรับเก็บผลรวมกับจำนวน ฯลฯ ในภาษาอย่าง C งานที่ต้องเขียนราว 6 บรรทัด ใน Q จบได้ด้วย 6 ตัวอักษรอย่างไรก็ตาม ทุกภาษาก็มีฟีเจอร์ที่ช่วยให้ให้เหตุผลกับปัญหาบางประเภทได้ดีขึ้น ภาษา functional ที่มี algebraic data type และ pattern matching เช่น OCaml หรือ F# ดีกว่า
switchหรือif-else-ifขนาดใหญ่ และภาษาที่มี syntax sugar อย่างasync/awaitก็ได้เปรียบในการจัดการ concurrencyตอนทำงานเป็นควอนต์ ผมใช้ kdb+/q อย่างหนักกับกลยุทธ์ความถี่ปานกลางมานานกว่า 5 ปี แต่พอย้ายไปทำ high-frequency trading ที่สิ่งอย่างการคำนวณ order book ไม่ได้ vectorize ได้ง่ายหรือมีประสิทธิภาพ การใช้ภาษาที่มี array เป็นศูนย์กลางต่อไปกลับทำให้การให้เหตุผลกับปัญหาซับซ้อนขึ้น
https://youtu.be/PlM9BXfu7UY?si=ORtwI1qmfmzhJGZX&t=3598
ส่วนนั้นอยู่ในบริบทของ compiler แต่ภาพรวมของการนำเสนอพูดถึง Dyalog และ APL ในฐานะระบบสัญกรณ์ทางคณิตศาสตร์ แกนหลักคือการ optimize นิพจน์คณิตศาสตร์อาจง่ายกว่า optimize โค้ดทั่วไป
หนึ่งในสิ่งที่สำคัญที่สุดตรงนี้คือ ตัวสร้างโจทย์ด้านบนนั้นชัดเจนมาก นี่คือความต่างระหว่าง ภาษาสัญลักษณ์แบบ Iverson ซึ่งรวมถึง J และ K กับภาษาอื่นๆ
แม้จะไม่มีความงดงามและพลังของวิธีแก้แบบบรรทัดเดียว แต่ก็เรียบร้อยและเข้าใจได้มากแม้ไม่มีคอมเมนต์อย่างเข้มงวด อย่างไรก็ตาม ผมคิดว่า
lampไม่ใช่สัญลักษณ์คอมเมนต์ที่ดีวิธีแก้แบบบรรทัดเดียวนั้นน่าทึ่ง และการเขียนโปรแกรมแบบ implicit ก็เจ๋งจนบิดสมอง แนวคิดที่ใช้ความกระชับเฉพาะตัวของภาษาที่อิง glyph เพื่ออธิบายและทำ functional programming แล้วนำสิ่งนั้นไปใช้กับอาร์เรย์ทั้งชุดอีกที เป็นความคิดระดับอัจฉริยะ
https://www.jsoftware.com/papers/fork.htm
แน่นอนว่าถ้าตัดความสามารถนั้นออก ก็อาจบังคับให้เขียนโค้ดยืดยาวขึ้นได้ แต่แบบนั้นจะลดจุดแข็งในฐานะเครื่องมือแบบโต้ตอบลงอย่างมาก ภาษาแบบ Iverson มีประโยชน์กับ งานแบบโต้ตอบ เพราะเขียนโค้ดที่สั้นมากได้ โค้ดในตอนนั้นถึงขั้นไม่ได้ถูกบันทึกไว้ด้วยซ้ำ จึงเป็นโค้ดแบบ write-only อย่างแท้จริง
เวลาจะเขียนโค้ดลงไฟล์ ก็เลือกสไตล์ที่ต้องการได้ และตอนนั้นแนะนำให้เขียนแบบไม่บีบอัดมากนัก ถึงอย่างนั้น ต่อให้เขียนภาษาแบบ Iverson ในสไตล์ยืดยาว ก็ยังให้โค้ดที่สั้นกว่าภาษาส่วนใหญ่อยู่ดี
คนส่วนใหญ่ไม่ชอบเพราะสัญลักษณ์ แต่ปัญหาของผมไม่ใช่ตรงนั้น
ผมชอบ APL และภาษาอาร์เรย์ และสิ่งที่ได้เรียนมาก็ช่วยได้มากเวลาใช้ภาษาอื่นๆ แต่สุดท้ายมันไม่ได้กลายเป็นเครื่องมือประจำวัน ไม่ใช่เพราะสัญลักษณ์ แต่เพราะหลังจากใช้งานแบบเป็นพักๆ ราว 3–4 ปี ผมชนกำแพงที่ก้าวข้ามไม่ได้
ในภาษาอื่นๆ มักจะมี แนวทางทั่วไป ที่ช่วยแก้ปัญหาไปแบบคร่าวๆ ได้ และภายหลังถ้าพบ “เคล็ดลับ” ของปัญหานั้น ก็ค่อยแก้ให้สง่างามและมีประสิทธิภาพขึ้นได้ แต่ APL ให้ความรู้สึกว่าไม่มีทางเลี่ยงชั่วคราวแบบนั้น มีแค่รู้เคล็ดลับหรือไม่รู้เท่านั้น
ผมไม่แน่ใจว่านั่นเป็นความจริงหรือเปล่า หรือถ้าเรียนเคล็ดลับมากพอแล้วจะเกิดสัญชาตญาณในการแก้ปัญหาขึ้นมา หรือท้ายที่สุดมันก็มีแต่เคล็ดลับอยู่ดี หรือผมแค่ไม่ได้อ่านเอกสารกลยุทธ์แกนหลักกันแน่
⍸⍣¯1ก็ได้แล้วไม่ใช่เหรอ?” แต่มีความเป็นไปได้สูงว่าไม่เคยมีใครบอกมาก่อนว่า⍸มีการดำเนินการผกผัน และใช้อย่างไรแม้ตอนนี้จะใช้ภาษาพวกนี้มาหลายปีแล้ว แต่กำแพงโค้ดที่โปรแกรมเมอร์อาร์เรย์บางคนสร้างขึ้นก็ยังทำให้รู้สึกหนักใจอยู่บ้าง เข้าใจว่าทำไมเขียนแบบนั้น แต่โดยส่วนตัวชอบให้โค้ดมีช่องว่างบ้าง
ผมกำลังสร้างภาษาอาร์เรย์ที่อิง APL อยู่ และหนึ่งในเป้าหมายช่วงแรกคือทำให้ สไตล์ imperative เป็นพลเมืองชั้นหนึ่ง โดยไม่ลงโทษมือใหม่ที่ใช้สิ่งอย่างคำสั่ง
ifผมมองว่าสไตล์นี้อยู่กึ่งกลางระหว่างแนว APL บริสุทธิ์กับภาษา imperative ทั่วไปhttps://codeberg.org/loke/array/src/branch/master/array/standard-lib/output3.kap
แต่ก็ไม่ใช่ข้อจำกัดของตัวภาษาเองเช่นกัน จากประสบการณ์ของผม กระบวนการทะลุกำแพงนั้นก็คือกระบวนการที่พาราไดม์เริ่มเข้าที่เข้าทาง หลังจากแฮ็กโปรโตไทป์ YAML parser อยู่ราว 500 ชั่วโมงตลอด 1 ปี ชิ้นส่วนต่างๆ จึงเริ่มประกอบกันได้
แก่นสำคัญดูเหมือนจะเป็นการผสมกันของหลักการออกแบบที่ขับเคลื่อนด้วยข้อมูล วิธีนำคุณสมบัติแบบ Iverson ของสัญกรณ์ที่ดีมาใช้ในสถาปัตยกรรมซอฟต์แวร์อย่างเป็นรูปธรรม และการคุ้นเคยกับ idiom รวมถึงวิธีที่มันแสดงแนวคิดของโดเมน
https://dyalog.tv/Dyalog23/?v=J4cg6SV92C4
https://www.jsoftware.com/papers/tot.htm
มีวิดีโอเกี่ยวกับหัวข้อนี้
https://www.youtube.com/watch?v=DmT80OseAGs
สามารถลองวิธีแก้ได้ด้วยตัวเองที่ https://tryapl.org/
ถ้าเปรียบเทียบบรรทัดเดียวนี้กับวิธีแก้แบบ code golf ในภาษาโปรแกรมต่างๆ ก็น่าจะน่าสนใจ
https://codegolf.stackexchange.com/questions/tagged/sudoku?tab=Votes
https://codegolf.stackexchange.com/a/5030