ECMAScript Regex, 733+ 690+ 158 119 118 (117🐌)ไบต์
ความสนใจใน regex ของฉันได้รับการกระตุ้นด้วยความแข็งแรงที่ได้รับการปรับปรุงใหม่หลังจากไม่มีการใช้งานนานกว่า4½ปี ดังนั้นฉันจึงค้นหาชุดตัวเลขและฟังก์ชั่นที่เป็นธรรมชาติมากขึ้นเพื่อให้ตรงกับ regexes ECMAScript ที่ยังไม่เสร็จปรับปรุงการพัฒนาเอนจิ้น regex ของฉันและเริ่มแปรงบน PCRE ด้วย
ฉันรู้สึกทึ่งกับความแปลกใหม่ของการสร้างฟังก์ชันทางคณิตศาสตร์ใน ECMAScript regex ปัญหาจะต้องได้รับการทาบทามจากมุมมองที่แตกต่างกันอย่างสิ้นเชิงและจนกว่าจะได้ข้อมูลเชิงลึกที่สำคัญทราบว่าพวกเขาสามารถแก้ไขได้ทั้งหมด มันบังคับให้มีเครือข่ายที่กว้างขึ้นมากในการค้นหาว่าคุณสมบัติทางคณิตศาสตร์ใดที่สามารถใช้เพื่อสร้างปัญหาเฉพาะที่แก้ไขได้
การจับคู่หมายเลขแฟคทอเรียลเป็นปัญหาที่ฉันไม่ได้คิดแม้แต่ในการแก้ปัญหาในปี 2014 - หรือถ้าฉันทำได้เพียงชั่วขณะหนึ่งก็ให้ไล่ออกเพราะไม่น่าจะเป็นไปได้ แต่เมื่อเดือนที่แล้วฉันรู้ว่ามันสามารถทำได้
เช่นเดียวกับการโพสต์ regex ECMA อื่น ๆ ของฉันฉันจะให้คำเตือน:ฉันขอแนะนำให้เรียนรู้วิธีการแก้ปัญหาทางคณิตศาสตร์ที่ไม่ธรรมดาใน ECMAScript regex มันเป็นการเดินทางที่น่าสนใจสำหรับฉันและฉันไม่ต้องการที่จะทำลายมันสำหรับใครก็ตามที่อาจต้องการลองด้วยตัวเองโดยเฉพาะผู้ที่มีความสนใจในทฤษฎีจำนวน ดูโพสต์ก่อนหน้านี้สำหรับรายการปัญหาที่แนะนำติดแท็กสปอยเลอร์ติดต่อกันเพื่อแก้ไขทีละรายการ
ดังนั้นไม่ได้อ่านเพิ่มเติมใด ๆ ถ้าคุณไม่ต้องการบางขั้นสูงมายากล regex เอกมากมายสำหรับคุณ หากคุณต้องการถ่ายภาพเพื่อหาเวทมนตร์นี้ด้วยตัวเองฉันขอแนะนำให้เริ่มต้นด้วยการแก้ปัญหาบางอย่างใน ECMAScript regex ดังที่อธิบายไว้ในโพสต์ที่ลิงก์ด้านบน
นี่คือความคิดของฉัน:
ปัญหาเกี่ยวกับการจับคู่ชุดหมายเลขนี้เช่นเดียวกับคนอื่น ๆ ส่วนใหญ่คือใน ECMA โดยปกติแล้วจะไม่สามารถติดตามการเปลี่ยนแปลงของตัวเลขสองตัวในวง บางครั้งพวกเขาสามารถ multiplexed (เช่นพลังของฐานเดียวกันสามารถเพิ่มเข้าด้วยกันอย่างไม่น่าสงสัย) แต่ขึ้นอยู่กับคุณสมบัติของพวกเขา ดังนั้นฉันจึงไม่สามารถเริ่มต้นด้วยหมายเลขอินพุตและหารด้วยการเพิ่มเงินปันผลที่เพิ่มขึ้นเรื่อย ๆ จนกว่าจะถึง 1 (หรืออย่างน้อยฉันก็คิดว่าอย่างน้อย)
จากนั้นฉันก็ทำการวิจัยเกี่ยวกับความหลากหลายของปัจจัยสำคัญในจำนวนแฟคทอเรียลและเรียนรู้ว่ามีสูตรสำหรับสิ่งนี้ - และมันเป็นสิ่งที่ฉันสามารถนำไปใช้ในระบบ ECMA regex!
หลังจาก stewing มันในขณะที่และสร้าง regexes อื่น ๆ ในระหว่างนี้ฉันเอางานเขียน regex factorial ใช้เวลาหลายชั่วโมง แต่สุดท้ายก็ทำงานได้ดี อัลกอรึทึมสามารถคืนค่าแฟกทอเรียลในรูปแบบของการแข่งขันได้ ไม่มีการหลีกเลี่ยงได้แม้แต่; โดยธรรมชาติของวิธีการที่จะต้องดำเนินการใน ECMA มันเป็นสิ่งจำเป็นที่จะคาดเดาสิ่งที่ปัจจัยผกผันคือก่อนที่จะทำอะไรอย่างอื่น
ข้อเสียคืออัลกอริธึมนี้ทำมานานมากสำหรับ regex ... แต่ฉันยินดีที่มันต้องใช้เทคนิคที่ใช้ใน regex คูณ 651 byte ของฉัน (อันที่ลงท้ายด้วยล้าสมัยเพราะวิธีการที่แตกต่างกันสำหรับ 50 byte regex) ฉันได้รับหวังว่าปัญหาจะปรากฏขึ้นที่ต้องใช้เคล็ดลับนี้: การดำเนินการกับตัวเลขสองซึ่งเป็นพลังทั้งสองของฐานเดียวกันในวงโดยการเพิ่มพวกเขาเข้าด้วยกันอย่างไม่น่าสงสัยและแยกพวกเขาในแต่ละรอบ
แต่เนื่องจากความยากลำบากและความยาวของอัลกอริทึมนี้ฉันจึงใช้ lookaheads ระดับโมเลกุล (จากรูปแบบ(?*...)
) เพื่อใช้งาน นั่นคือคุณลักษณะที่ไม่ได้อยู่ใน ECMAScript หรือเครื่องมือหลัก regex อื่น ๆ แต่อย่างหนึ่งที่ผมได้นำมาใช้ในเครื่องยนต์ของฉัน หากไม่มีการจับภาพใด ๆ ภายในโมเลกุลลุดเฮดก็สามารถใช้งานได้เทียบเท่ากับอะตอมลีดเดอร์อะตอม แต่ด้วยการจับมันอาจทรงพลังมาก เครื่องยนต์จะย้อนกลับไปที่ lookahead และสิ่งนี้สามารถใช้ในการคาดเดาค่าซึ่งวนรอบผ่านความเป็นไปได้ทั้งหมด (สำหรับการทดสอบในภายหลัง) โดยไม่ต้องใช้อักขระของอินพุต การใช้มันสามารถทำให้เกิดการใช้ที่สะอาดยิ่งขึ้น (Lookbe-length lookbehind เป็นอย่างน้อยที่สุดเท่าที่เท่าเทียมกันในการใช้พลังงานกับโมเลกุล lookahead แต่หลังมีแนวโน้มที่จะทำให้การใช้งานตรงไปตรงมาและสง่างามมากขึ้น)
ดังนั้นความยาว 733 และ 690 ไบต์จึงไม่ได้เป็นตัวแทนของ ECMAScript ที่เข้ากันได้ของโซลูชัน - ดังนั้นจึงมีเครื่องหมาย "+" ตามหลัง แน่นอนว่าเป็นไปได้ที่จะทำการอัลกอริธึมนั้นให้บริสุทธิ์ ECMAScript (ซึ่งจะเพิ่มความยาวได้ไม่มากนัก) แต่ฉันไม่ได้เข้าใกล้มัน ... เพราะฉันคิดว่าอัลกอริทึมที่ง่ายและกะทัดรัดมากขึ้น! หนึ่งที่สามารถดำเนินการได้อย่างง่ายดายโดยไม่ต้องโมเลกุล lookaheads มันเร็วกว่ามากเช่นกัน
ใหม่นี้ก่อนหน้านี้จะต้องเดาที่ปัจจัยผกผันขี่จักรยานผ่านความเป็นไปได้ทั้งหมดและทดสอบพวกเขาสำหรับการแข่งขัน มันหาร N ด้วย 2 เพื่อให้มีที่ว่างสำหรับงานที่ต้องทำแล้วจึงวนซ้ำซึ่งจะแบ่งการป้อนข้อมูลซ้ำโดยตัวหารที่เริ่มต้นที่ 3 และเพิ่มขึ้นในแต่ละครั้ง (เช่น 1! และ 2! ไม่สามารถจับคู่โดยอัลกอริทึมหลักและต้องจัดการแยกต่างหาก) ตัวหารจะถูกติดตามโดยการเพิ่มลงในความฉลาดทางวิ่ง ตัวเลขสองตัวนี้สามารถแยกออกได้อย่างไม่น่าเชื่อเพราะสมมุติว่า M! == N หารด้วยผลหารจะยังคงถูกหารด้วย M จนกว่าจะเท่ากับ M
regex นี้ทำการหารด้วยตัวแปรในส่วนด้านในสุดของลูป อัลกอริธึมการหารเหมือนกับใน regexes อื่น ๆ ของฉัน (และคล้ายกับอัลกอริธึมการคูณ): สำหรับA≤B, A * B = C หากมีเฉพาะถ้า C% A = 0 และ B เป็นจำนวนมากที่สุดซึ่งตรงกับ B satisfC และ C% B = 0 และ (CB- (A-1))% (B-1) = 0 โดยที่ C คือเงินปันผล A คือตัวหารและ B คือผลหาร (อัลกอริทึมที่คล้ายกันสามารถใช้กับกรณีที่A≥Bและหากไม่ทราบว่า A เปรียบเทียบกับ B ได้อย่างไรการทดสอบการหารพิเศษหนึ่งรายการเป็นสิ่งที่จำเป็น)
ดังนั้นฉันจึงรักว่าปัญหานั้นสามารถลดความซับซ้อนลงให้น้อยลงกว่าฟีโบนักชีเรกซ์ที่ได้รับการปรับปรุงให้ดีขึ้น แต่ฉันถอนหายใจด้วยความผิดหวังว่าเทคนิคมัลติเพลกซ์พาวเวอร์ - ของ - ของ - ฐานเดียวกันจะต้องรอปัญหาอื่น ที่จริงต้องการมันเพราะอันนี้ไม่ได้ มันเป็นเรื่องราวของอัลกอริทึมการคูณ 651 ไบต์ของฉันที่ถูกแทนที่ด้วย 50 ไบต์อีกครั้ง!
แก้ไข: ฉันสามารถดรอป 1 ไบต์ (119 → 118) โดยใช้กลอุบายที่Grimyพบซึ่งสามารถทำให้การหารสั้นลงในกรณีที่รับประกันความฉลาดทางนั้นมากกว่าหรือเท่ากับตัวหาร
ด้วยความกังวลใจต่อไปนี่คือ regex:
รุ่นจริง / เท็จ (118 ไบต์):
^((x*)x*)(?=\1$)(?=(xxx\2)+$)((?=\2\3*(x(?!\3)xx(x*)))\6(?=\5+$)(?=((x*)(?=\5(\8*$))x)\7*$)x\9(?=x\6\3+$))*\2\3$|^xx?$
ลองออนไลน์!
ส่งคืนปัจจัยแบบตรงข้ามหรือไม่จับคู่ (124 ไบต์)
^(?=((x*)x*)(?=\1$)(?=(xxx\2)+$)((?=\2\3*(x(?!\3)xx(x*)))\6(?=\5+$)(?=((x*)(?=\5(\8*$))x)\7*$)x\9(?=x\6\3+$))*\2\3$)\3|^xx?$
ลองออนไลน์!
ส่งคืนปัจจัยแบบตรงข้ามหรือไม่จับคู่ในECMAScript +\K
(120 ไบต์):
^((x*)x*)(?=\1$)(?=(xxx\2)+$)((?=\2\3*(x(?!\3)xx(x*)))\6(?=\5+$)(?=((x*)(?=\5(\8*$))x)\7*$)x\9(?=x\6\3+$))*\2\K\3$|^xx?$
และเวอร์ชันที่เว้นว่างพร้อมความคิดเห็น:
^
(?= # Remove this lookahead and the \3 following it, while
# preserving its contents unchanged, to get a 119 byte
# regex that only returns match / no-match.
((x*)x*)(?=\1$) # Assert that tail is even; \1 = tail / 2;
# \2 = (conjectured N for which tail == N!)-3; tail = \1
(?=(xxx\2)+$) # \3 = \2+3 == N; Assert that tail is divisible by \3
# The loop is seeded: X = \1; I = 3; tail = X + I-3
(
(?=\2\3*(x(?!\3)xx(x*))) # \5 = I; \6 = I-3; Assert that \5 <= \3
\6 # tail = X
(?=\5+$) # Assert that tail is divisible by \5
(?=
( # \7 = tail / \5
(x*) # \8 = \7-1
(?=\5(\8*$)) # \9 = tool for making tail = \5\8
x
)
\7*$
)
x\9 # Prepare the next iteration of the loop: X = \7; I += 1;
# tail = X + I-3
(?=x\6\3+$) # Assert that \7 is divisible by \3
)*
\2\3$
)
\3 # Return N, the inverse factorial, as a match
|
^xx?$ # Match 1 and 2, which the main algorithm can't handle
ประวัติที่สมบูรณ์ของการเพิ่มประสิทธิภาพกอล์ฟของฉันของ regexes เหล่านี้อยู่ใน GitHub:
regex สำหรับการจับคู่หมายเลขแฟกทอเรียล - วิธีการเปรียบเทียบหลายหลากพร้อมด้วย lookahead.txt โมเลกุล
regex สำหรับการจับคู่แฟคทอเรียล number.txt (ที่แสดงด้านบน)
((x*)x*)
((x*)+)
((x+)+)
n = 3 !\2
3 - 3 = 0
เอ็นจิ้น. NET regex ไม่เลียนแบบพฤติกรรมนี้ในโหมด ECMAScript ของมันและทำให้ 117 byte regex ทำงาน:
ลองออนไลน์! (รุ่นเอ็กซ์โพเนนเชียล - ช้าลงพร้อมด้วย. NET regex engine + ECMAScript emulation)
1
อย่างไร