อัลกอริธึมการค้นหาสตริงย่อยที่เร็วที่สุดคืออะไร


165

ตกลงดังนั้นฉันไม่ฟังเหมือนคนงี่เง่าฉันจะระบุปัญหา / ข้อกำหนดเพิ่มเติมอย่างชัดเจน:

  • Needle (pattern) และ haystack (text to search) เป็นทั้งสตริง C-style ที่สิ้นสุดด้วยค่า null ไม่มีข้อมูลความยาวให้; หากจำเป็นจะต้องคำนวณ
  • ฟังก์ชั่นควรกลับตัวชี้ไปที่การแข่งขันครั้งแรกหรือNULLหากไม่พบการแข่งขัน
  • ไม่อนุญาตให้ใช้กรณีที่ล้มเหลว ซึ่งหมายความว่าอัลกอริทึมใด ๆ ที่มีความต้องการพื้นที่เก็บข้อมูลไม่คงที่ (หรือขนาดใหญ่คงที่) จะต้องมีกรณีสำรองสำหรับความล้มเหลวในการจัดสรร
  • การใช้งานจะต้องอยู่ใน C แม้ว่าคำอธิบายที่ดีของอัลกอริทึม

... รวมถึงสิ่งที่ฉันหมายถึงโดย "เร็วที่สุด":

  • กำหนดO(n)ที่n= ความยาวกองหญ้า (แต่อาจเป็นไปได้ที่จะใช้แนวคิดจากอัลกอริทึมซึ่งโดยปกติO(nm)(ตัวอย่างเช่นแฮชการกลิ้ง) หากรวมกับอัลกอริธึมที่แข็งแกร่งกว่าเพื่อให้ได้O(n)ผลลัพธ์ที่กำหนดขึ้นมา
  • ไม่เคยทำ (วัดได้; นาฬิกาสองสามนาฬิกาif (!needle[1])ฯลฯ ไม่เป็นไร) แย่กว่าอัลกอริธึมกำลังเดรัจฉานไร้เดียงสาโดยเฉพาะอย่างยิ่งในเข็มสั้น ๆ ซึ่งน่าจะเป็นกรณีที่พบบ่อยที่สุด (ค่าโสหุ้ยการประมวลผลล่วงหน้าที่ไม่มีเงื่อนไขอย่างหนักนั้นไม่ดีเช่นเดียวกับที่พยายามปรับปรุงค่าสัมประสิทธิ์เชิงเส้นสำหรับเข็มทางพยาธิวิทยาด้วยค่าใช้จ่ายของเข็มที่มีแนวโน้ม)
  • ได้รับเข็มและกองหญ้าโดยพลการประสิทธิภาพเทียบเคียงหรือดีกว่า (ไม่เลวร้ายยิ่งกว่าการค้นหานานกว่า 50%) เทียบกับอัลกอริทึมที่ใช้งานกันอย่างแพร่หลายอื่น ๆ
  • นอกเหนือจากเงื่อนไขเหล่านี้ฉันยังคงนิยามคำว่า "เร็วที่สุด" ที่เปิดกว้าง คำตอบที่ดีควรอธิบายว่าทำไมคุณจึงพิจารณาวิธีที่คุณแนะนำว่า "เร็วที่สุด"

การใช้งานปัจจุบันของฉันทำงานช้าลงประมาณ 10% และเร็วขึ้น 8 เท่า (ขึ้นอยู่กับอินพุต) กว่าการใช้ Two-Way ของ glibc

อัปเดต: อัลกอริทึมที่เหมาะสมที่สุดของฉันในปัจจุบันเป็นดังนี้:

  • สำหรับเข็มมีความยาว 1 strchrใช้
  • สำหรับเข็มที่มีความยาว 2-4 ให้ใช้คำของเครื่องเพื่อเปรียบเทียบ 2-4 ไบต์พร้อมกันดังนี้: โหลดเข็มในจำนวนเต็ม 16- หรือ 32- บิตพร้อมกับเปลี่ยนบิตและวงจรไบต์เก่าออก / ใหม่ไบต์จากกองหญ้าในแต่ละการทำซ้ำ . ทุกไบต์ของกองหญ้าจะถูกอ่านอย่างแน่นอนหนึ่งครั้งและเกิดการตรวจสอบกับ 0 (จุดสิ้นสุดของสตริง) และการเปรียบเทียบแบบ 16 หรือ 32 บิตหนึ่งรายการ
  • สำหรับเข็มที่มีความยาว> 4 ให้ใช้อัลกอริทึมแบบสองทางพร้อมกับตารางการเลื่อนที่ไม่ดี (เช่น Boyer-Moore) ซึ่งใช้กับไบต์สุดท้ายของหน้าต่างเท่านั้น เพื่อหลีกเลี่ยงค่าใช้จ่ายในการเริ่มต้นตาราง 1kb ซึ่งจะเป็นผลขาดทุนสุทธิสำหรับเข็มยาวปานกลางจำนวนมากฉันเก็บบิตอาร์เรย์ (32 ไบต์) ที่ทำเครื่องหมายว่ารายการใดในตารางเลื่อนถูกเตรียมใช้งาน บิตที่ไม่ได้ตั้งค่านั้นสอดคล้องกับค่าไบต์ที่ไม่เคยปรากฏในเข็มซึ่งเป็นไปได้ที่การเปลี่ยนความยาวเข็มเต็มเป็นไปได้

คำถามใหญ่ที่เหลืออยู่ในใจของฉันคือ:

  • มีวิธีการใช้ตารางกะที่ดีกว่านี้หรือไม่? Boyer-Moore ใช้งานได้อย่างดีที่สุดโดยการสแกนไปทางด้านหลัง (ขวาไปซ้าย) แต่ Two-Way ต้องการการสแกนจากซ้ายไปขวา
  • เพียงสองขั้นตอนวิธีการทำงานของผู้สมัครที่ฉันได้พบสำหรับกรณีทั่วไป (ไม่ออกจากหน่วยความจำหรือประสิทธิภาพการทำงานของกำลังสองเงื่อนไข) เป็นTwo-Wayและการจับคู่สายในตัวอักษรที่ได้รับคำสั่ง แต่มีกรณีที่ตรวจพบได้ง่ายซึ่งอัลกอริธึมที่แตกต่างกันเหมาะสมหรือไม่ แน่นอนว่าหลาย ๆO(m)(ที่mความยาวของเข็ม) ในอัลกอริทึมของอวกาศสามารถนำมาใช้เพื่อการm<100นั้นได้ นอกจากนี้ยังเป็นไปได้ที่จะใช้อัลกอริธึมที่เป็นกำลังสองกรณีที่เลวร้ายที่สุดถ้ามีการทดสอบง่าย ๆ สำหรับเข็มที่ต้องการเวลาเชิงเส้นเท่านั้น

คะแนนโบนัสสำหรับ:

  • คุณสามารถปรับปรุงประสิทธิภาพโดยการสมมติว่าเข็มและกองหญ้านั้นเป็น UTF-8 ที่มีรูปแบบที่ดีหรือไม่? (ด้วยอักขระที่มีความยาวไบต์ต่างกันรูปแบบที่ดีจะกำหนดข้อกำหนดการจัดเรียงสตริงบางส่วนระหว่างเข็มและกองหญ้าและอนุญาตให้มีการเปลี่ยน 2-4 ไบต์อัตโนมัติเมื่อพบหัวไบต์ที่ไม่ตรงกัน แต่ข้อ จำกัด เหล่านี้ทำให้คุณซื้ออะไรมากไป การคำนวณคำต่อท้ายสูงสุดการเปลี่ยนแปลงคำต่อท้ายที่ดี ฯลฯ ให้อัลกอริธึมต่างๆแล้วหรือยัง?)

หมายเหตุ:ฉันตระหนักดีถึงอัลกอริธึมส่วนใหญ่ที่นั่นไม่ใช่ว่าพวกเขาปฏิบัติได้ดีเพียงใด ต่อไปนี้เป็นข้อมูลอ้างอิงที่ดีเพื่อให้ผู้คนไม่ให้การอ้างอิงฉันเกี่ยวกับอัลกอริทึมเป็นความคิดเห็น / คำตอบ: http://www-igm.univ-mlv.fr/~lecroq/string/index.html


ค่อนข้างมีจำนวนของขั้นตอนวิธีการค้นหาสตริงที่ระบุไว้บนเป็นอัลกอริทึมในสาย คุณอาจต้องการอธิบายอัลกอริทึมที่คุณพิจารณาจากรายการนี้
เกร็กฮิวกิล

61
ลิงค์ในตอนท้ายนั้นคือทองคำ!
Carlos

4
ฉันไม่อยากจะเชื่อว่าคุณยังไม่ได้รับคำตอบ
user541686

1
@ Mehrdad: ฉันกำลังจะบอกว่าไม่มีคำตอบใด ๆ ที่ตอบคำถามตามที่ถามจริง ๆ แต่ดูเหมือนว่าคุณจะ ในเวลาที่คุณตอบว่าฉันเดินหน้าต่อไปและปล่อยให้การปรับปรุงstrstrในภายหลังเป็นอะไรบางอย่างในภายหลังดังนั้นฉันจึงไม่ได้อ่านบทความที่คุณเชื่อมโยงอย่างถูกต้อง แต่มันฟังดูดีมาก ขอขอบคุณและขอโทษที่ไม่ติดต่อคุณ
. GitHub หยุดช่วยน้ำแข็ง

คำตอบ:


37

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

Boyer-Mooreใช้ตารางตัวละครที่ไม่ดีกับตารางต่อท้ายที่ดี

Boyer-Moore-Horspoolใช้ตารางตัวละครที่ไม่ดี

Knuth-Morris-Prattใช้ตารางการแข่งขันบางส่วน

Rabin-Karpใช้แฮชที่กำลังรันอยู่

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

แก้ไข:

อัลกอริทึมที่แตกต่างกันอาจดีที่สุดสำหรับการค้นหาคู่เบสวลีภาษาอังกฤษหรือคำเดียว หากมีหนึ่งอัลกอริทึมที่ดีที่สุดสำหรับอินพุตทั้งหมดมันจะถูกเผยแพร่

คิดเกี่ยวกับตารางเล็ก ๆ น้อย ๆ ต่อไปนี้ เครื่องหมายคำถามแต่ละข้ออาจมีวิธีการค้นหาที่ดีที่สุดที่แตกต่างกัน

                 short needle     long needle
short haystack         ?               ?
long haystack          ?               ?

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

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

ฉันทิ้งระยะสั้นและระยะยาวคลุมเครือ สำหรับเข็มนั้นฉันคิดว่าสั้นไม่เกิน 8 ตัวอักษรปานกลางถึง 64 ตัวอักษรและยาวต่ำกว่า 1k สำหรับกองหญ้านั้นฉันคิดว่าตัวเตี้ยที่ต่ำกว่า 2 ^ 10, ปานกลางเท่ากับต่ำกว่า 2 ^ 20 และยาวได้ถึง 2 ^ 30 ตัวอักษร


1
คุณมีคำแนะนำที่ดีสำหรับห้องสมุดทดสอบหรือไม่? คำถามก่อนหน้านี้ที่ฉันถามเกี่ยวกับ SO นั้นเกี่ยวข้องกับสิ่งนั้นและฉันไม่เคยได้รับคำตอบที่แท้จริงเลย (ยกเว้นของฉันเอง ... ) มันควรจะกว้างขวาง แม้ว่าความคิดของฉันเกี่ยวกับแอปพลิเคชั่นสำหรับ strstr คือการค้นหาข้อความภาษาอังกฤษคนอื่นอาจกำลังค้นหายีนในลำดับคู่เบส ...
R. GitHub STOP ช่วย ICE

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

31

เผยแพร่ในปี 2011 ฉันเชื่อว่ามันอาจเป็นอัลกอริทึม"Simple Real-Time String- อวกาศคงที่" การจับคู่โดย Dany Breslauer, Roberto Grossi และ Filippo Mignosi

ปรับปรุง:

ในปี 2014 ผู้เขียนที่ตีพิมพ์การปรับปรุงนี้ในช่วงที่ดีที่สุดบรรจุจับคู่สตริง


1
ว้าวขอบคุณ. ฉันกำลังอ่านกระดาษ ถ้ามันดีกว่าสิ่งที่ฉันมีฉันจะยอมรับคำตอบของคุณอย่างแน่นอน
. GitHub หยุดช่วยน้ำแข็ง

1
@R .. : แน่นอน! :) ถ้าคุณจัดการเพื่อใช้อัลกอริทึมโปรดพิจารณาการโพสต์บน StackOverflow เพื่อให้ทุกคนได้รับประโยชน์จากมัน! ฉันไม่พบการติดตั้งใช้งานที่ใดก็ได้และฉันก็ไม่เก่งในการใช้อัลกอริทึมที่พบในรายงานการวิจัยฮ่าฮ่า
user541686

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

11
และคุณยังไม่ยอมรับคำตอบของ @ Mehrdad! :-)
lifebalance

3
@DavidWallace: อะไรนะ มันมีชื่อบทความและผู้แต่ง แม้ว่าลิงก์จะตายคุณก็สามารถค้นหาเอกสารได้ คุณคาดหวังให้ฉันทำอะไรเขียนโค้ดปลอมสำหรับอัลกอริทึม? อะไรทำให้คุณคิดว่าฉันเข้าใจอัลกอริทึม?
user541686

23

http://www-igm.univ-mlv.fr/~lecroq/string/index.html เชื่อมโยงคุณชี้ไปที่เป็นแหล่งที่ดีและการสรุปของบางส่วนของที่รู้จักกันดีและวิจัยขั้นตอนวิธีการจับคู่สาย

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

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

ใช้เวลาทบทวนจุดแข็งและจุดอ่อนเฉพาะของอัลกอริทึมที่คุณอ้างอิงไปแล้ว ดำเนินการตรวจสอบโดยมีวัตถุประสงค์ในการค้นหาชุดของอัลกอริทึมที่ครอบคลุมช่วงและขอบเขตของการค้นหาสตริงที่คุณสนใจจากนั้นสร้างตัวเลือกการค้นหาส่วนหน้าตามฟังก์ชั่นลักษณนามเพื่อกำหนดเป้าหมายที่ดีที่สุดสำหรับอินพุตที่กำหนด วิธีนี้คุณอาจใช้อัลกอริทึมที่มีประสิทธิภาพที่สุดในการทำงาน สิ่งนี้มีประสิทธิภาพเป็นพิเศษเมื่ออัลกอริทึมดีมากสำหรับการค้นหาบางอย่าง แต่ลดระดับลงอย่างไม่ดีนัก ยกตัวอย่างเช่นกำลังดุร้ายอาจจะดีที่สุดสำหรับเข็มมีความยาว 1 แต่อย่างรวดเร็ว degrades ความยาวเข็มเพิ่มขึ้นครั้นแล้วsustik-moore algoritimอาจมีประสิทธิภาพมากขึ้น (มากกว่าตัวอักษรขนาดเล็ก) จากนั้นสำหรับเข็มที่ยาวกว่าและตัวอักษรที่ใหญ่ขึ้นอัลกอริทึม KMP หรือ Boyer-Moore อาจดีกว่า นี่เป็นเพียงตัวอย่างเพื่อแสดงให้เห็นถึงกลยุทธ์ที่เป็นไปได้

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

อัลกอริธึมการค้นหาแต่ละรายการมีหลายรูปแบบที่สามารถสร้างความแตกต่างอย่างมีนัยสำคัญต่อประสิทธิภาพการทำงานตัวอย่างเช่นเอกสารนี้แสดงให้เห็น

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


1
ขอบคุณสำหรับคำตอบโดยเฉพาะลิงก์ไปยัง Sustik-Moore ที่ฉันไม่เคยเห็นมาก่อน วิธีอัลกอริทึมหลายวิธีนั้นมีการใช้อย่างแพร่หลาย Glibc โดยทั่วไปจะใช้ strchr, Two-Way โดยไม่มีตาราง shift character ที่ไม่ดีหรือ Two-Way ที่มีตาราง shift character ที่ไม่ดีขึ้นอยู่กับว่า needle_len เป็น 1, <32 หรือ> 32 วิธีการปัจจุบันของฉันเหมือนกันยกเว้นว่าฉันใช้ตาราง shift เสมอ ฉันแทนที่ memke ขนาด 1kb ที่จำเป็นในการทำเช่นนั้นด้วย 32 byte memset บน bitset ที่ใช้เพื่อทำเครื่องหมายว่าองค์ประกอบใดของตารางที่ถูกกำหนดค่าเริ่มต้นและฉันได้รับประโยชน์
. GitHub หยุดช่วย ICE

1
หลังจากคิดเกี่ยวกับมันฉันอยากรู้จริงๆว่าแอปพลิเคชันที่ตั้งใจไว้สำหรับ Sustik-Moore คืออะไร ด้วยตัวอักษรขนาดเล็กคุณจะไม่มีวันเปลี่ยนแปลงอย่างมีนัยสำคัญ (ตัวอักษรทั้งหมดเกือบจะปรากฏขึ้นใกล้กับจุดสิ้นสุดของเข็ม) และวิธีออโตมาตา จำกัด มีประสิทธิภาพมาก (ตารางการเปลี่ยนสถานะขนาดเล็ก) ดังนั้นฉันจึงไม่สามารถจินตนาการได้ว่าสถานการณ์ใดที่ Sustik-Moore จะเหมาะสมที่สุด ...
.. GitHub หยุดช่วย ICE

การตอบสนองที่ดี - ถ้าฉันสามารถแสดงคำตอบนี้ได้
Jason S

1
@R .. ทฤษฎีที่อยู่เบื้องหลังอัลกอริธึม sustik-moore คือมันควรจะให้ปริมาณกะเฉลี่ยที่มากขึ้นเมื่อเข็มมีขนาดค่อนข้างใหญ่และตัวอักษรมีขนาดค่อนข้างเล็ก (เช่นการค้นหาลำดับดีเอ็นเอ) ใหญ่กว่าในกรณีนี้หมายถึงใหญ่กว่าอัลกอริธึม Boyer-Moore พื้นฐานที่จะสร้างให้ได้รับอินพุตเดียวกัน วิธีนี้มีประสิทธิภาพมากขึ้นเมื่อเทียบกับวิธีออโตมาตา จำกัด หรือการเปลี่ยนแปลง Boyer-Moore อื่น ๆ (ซึ่งมีจำนวนมาก) ยากที่จะพูด นั่นคือเหตุผลที่ฉันเน้นการใช้เวลาในการวิจัยจุดแข็ง / จุดอ่อนเฉพาะของอัลกอริทึมผู้สมัครของคุณ
NealB

1
อืมฉันคิดว่าฉันคงคิดที่จะกะในความรู้สึกของตัวละครที่ไม่ดีจากบอยเยอร์ - มัวร์ ด้วยการปรับปรุงการเปลี่ยนแปลงส่วนต่อท้ายที่ดีของ BM แต่ Sustik-Moore อาจดีกว่าแนวทาง DFA ในการค้นหา DNA สิ่งที่เรียบร้อย
.. GitHub หยุดช่วย ICE

21

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

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

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

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


1
คุณรู้จักการใช้ C หรือ C ++ หรือไม่ ฉันคิดว่าจะใช้สิ่งนี้เพื่อการค้นหาแรงจูงใจของดีเอ็นเอ ถ้าไม่ฉันอาจลองพัฒนาการใช้งานด้วยตนเองและส่งเพื่อเพิ่มอัลกอริทึม
JDiMatteo

4
ดูเหมือนว่าจะไม่มีการใช้งานที่มีอยู่อัลกอริทึม Sustik-Moore / 2BLOCK ดูเหมือนจะไม่ถูกนำมาใช้ในทางปฏิบัติและยังคงถูกตัดออกจากผลลัพธ์ในเอกสารสรุปเช่น"ปัญหาการจับคู่สตริงที่แน่นอน: การประเมินทดลองแบบครอบคลุม"
JDiMatteo

18

อัลกอริทึมการค้นหาสตริงย่อยที่เร็วที่สุดจะขึ้นอยู่กับบริบท:

  1. ขนาดตัวอักษร (เช่น DNA vs English)
  2. ความยาวของเข็ม

กระดาษ 2010 "ปัญหาการจับคู่สตริงที่แน่นอน: การประเมินการทดลองที่ครอบคลุม"ให้ตารางที่มี runtimes สำหรับอัลกอริทึม 51 ตัว (ที่มีขนาดตัวอักษรและความยาวเข็มต่างกัน) ดังนั้นคุณสามารถเลือกอัลกอริทึมที่ดีที่สุดสำหรับบริบทของคุณ

อัลกอริทึมเหล่านั้นทั้งหมดมีการใช้งาน C เช่นเดียวกับชุดทดสอบที่นี่:

http://www.dmi.unict.it/~faro/smart/algorithms.php


4

เป็นคำถามที่ดีจริงๆ เพียงเพิ่มบิตเล็ก ๆ ...

  1. มีคนกำลังพูดถึงการจับคู่ลำดับดีเอ็นเอ แต่สำหรับลำดับ DNA สิ่งที่เรามักจะทำคือสร้างโครงสร้างข้อมูล (เช่นส่วนต่อท้ายต้นไม้ต่อท้ายหรือดัชนี FM) สำหรับกองหญ้าและจับคู่เข็มกับมัน นี่เป็นคำถามที่แตกต่าง

  2. มันจะดีจริงๆถ้ามีคนต้องการเปรียบเทียบอัลกอริทึมต่าง ๆ มีเกณฑ์มาตรฐานที่ดีมากในการบีบอัดและการสร้างอาร์เรย์ต่อท้าย แต่ฉันไม่เคยเห็นมาตรฐานในการจับคู่สตริง ผู้สมัครที่กองหญ้าที่อาจเกิดขึ้นอาจจะมาจากมาตรฐาน SACA

  3. ไม่กี่วันที่ผ่านมาฉันกำลังทดสอบการใช้งาน Boyer-Moore จากหน้าเว็บที่คุณแนะนำ (แก้ไข: ฉันต้องการฟังก์ชั่นการโทรเช่น memmem () แต่มันไม่ใช่ฟังก์ชั่นมาตรฐานดังนั้นฉันจึงตัดสินใจใช้งาน) โปรแกรมการเปรียบเทียบของฉันใช้กองฟางแบบสุ่ม ดูเหมือนว่าการติดตั้ง Boyer-Moore ในหน้านั้นเร็วกว่า memmem ของ glibc () และ strnstr ของ Mac () ในกรณีที่คุณมีความสนใจการดำเนินการเป็นที่นี่และรหัสการเปรียบเทียบเป็นที่นี่ นี่ไม่ใช่เกณฑ์มาตรฐานที่เหมือนจริงอย่างแน่นอน แต่เป็นการเริ่มต้น


หากคุณมีเข็มที่ดีในการทดสอบพร้อมกับผู้สมัครในกองหญ้าจากเกณฑ์มาตรฐาน SACA โพสต์พวกเขาเป็นคำตอบสำหรับคำถามอื่น ๆของฉันและไม่ได้รับคำตอบที่ดีกว่าฉันจะทำเครื่องหมายว่ายอมรับ
. GitHub หยุดช่วยน้ำแข็ง

3
เกี่ยวกับ memmem และ Boyer-Moore ของคุณเป็นไปได้มากว่า Boyer-Moore (หรือมากกว่าหนึ่งในการปรับปรุง Boyer-Moore) จะทำงานได้ดีที่สุดกับข้อมูลแบบสุ่ม ข้อมูลสุ่มมีความน่าจะเป็นที่ต่ำมากในช่วงเวลาและการแข่งขันบางส่วนที่ยาวซึ่งนำไปสู่ ฉันกำลังมองหาวิธีที่จะรวม Boyer-Moore และ Two-Way หรือตรวจจับอย่างมีประสิทธิภาพเมื่อ Boyer-Moore เป็น "ปลอดภัยในการใช้" แต่จนถึงตอนนี้ฉันยังไม่เคยประสบความสำเร็จ BTW ฉันจะไม่ใช้ memmem ของ glibc เป็นการเปรียบเทียบ การนำของฉันไปใช้งานโดยพื้นฐานแล้วอัลกอริธึมเดียวกันกับ glibc นั้นเร็วขึ้นหลายเท่า
. GitHub หยุดช่วยน้ำแข็ง

อย่างที่ฉันพูดไปมันไม่ได้ใช้งานเลย ขอมอบเครดิตให้กับ Christian Charras และ Thierry Lecroq ฉันสามารถจินตนาการได้ว่าเหตุใดการป้อนข้อมูลแบบสุ่มจึงไม่ดีสำหรับการเปรียบเทียบและฉันมั่นใจว่า glibc เลือกอัลกอริทึมด้วยเหตุผล ฉันยังเดา memmem () ไม่ได้ดำเนินการอย่างมีประสิทธิภาพ ฉันจะพยายาม. ขอบคุณ
user172818

4

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


3

ใช้ stdlib strstr:

char *foundit = strstr(haystack, needle);

มันเร็วมากเพียงแค่ฉันใช้เวลาประมาณ 5 วินาทีในการพิมพ์


26
และถ้าคุณอ่านคำถามของฉันคุณจะเห็นว่าฉันมีเวลาค่อนข้างง่ายกว่ามัน ฉันชอบการถากถางของคุณพอที่ฉันจะข้าม -1 แม้ว่า
. GitHub หยุดช่วยน้ำแข็ง

3

นี่คือการใช้การค้นหาของงูใหญ่ที่ใช้จากทั่วทุกแกน ความคิดเห็นที่แสดงให้เห็นว่ามันใช้การบีบอัดบอยเยอร์มัวร์เดลต้าตารางที่ 1

ฉันได้ทำการทดลองอย่างกว้างขวางด้วยการค้นหาสตริงด้วยตัวเอง แต่เป็นการค้นหาหลายครั้ง การใช้งานในแอสเซมบลีของHorspoolและBitapสามารถจับอัลกอริทึมของตนเองเช่นAho-Corasickเพื่อนับจำนวนรูปแบบที่ต่ำ


3

strchrอัลกอริทึม"ค้นหาอักขระที่ตรงกัน" (ala ) ที่เร็วขึ้น

หมายเหตุสำคัญ:

  • ฟังก์ชั่นเหล่านี้ใช้ "จำนวน / นับ (ชั้นนำ | ต่อท้าย) ศูนย์" gccคอมไพเลอร์ __builtin_ctzintrinsic- ฟังก์ชั่นเหล่านี้มีแนวโน้มที่จะทำงานได้อย่างรวดเร็วบนเครื่องที่มีคำสั่งที่ดำเนินการนี้ (เช่น x86, ppc, arm)

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

  • ฟังก์ชั่นเหล่านี้เป็นโปรเซสเซอร์ที่เป็นกลาง หาก CPU เป้าหมายมีคำแนะนำเวกเตอร์คุณอาจทำได้ดีขึ้น (มาก) ยกตัวอย่างเช่นstrlenฟังก์ชั่นการใช้งานด้านล่าง SSE3 และสามารถปรับเปลี่ยนนิด ๆ เพื่อ XOR 0ไบต์สแกนที่จะมองหาไบต์อื่นที่ไม่ใช่ มาตรฐานทำงานบนแล็ปท็อป 2.66GHz Core 2 ที่ใช้ Mac OS X 10.6 (x86_64):

    • 843.433 MB / s สำหรับ strchr
    • 2656.742 MB / s สำหรับ findFirstByte64
    • 13094.479 MB / s สำหรับ strlen

... รุ่น 32 บิต:

#ifdef __BIG_ENDIAN__
#define findFirstZeroByte32(x) ({ uint32_t _x = (x); _x = ~(((_x & 0x7F7F7F7Fu) + 0x7F7F7F7Fu) | _x | 0x7F7F7F7Fu); (_x == 0u)   ? 0 : (__builtin_clz(_x) >> 3) + 1; })
#else
#define findFirstZeroByte32(x) ({ uint32_t _x = (x); _x = ~(((_x & 0x7F7F7F7Fu) + 0x7F7F7F7Fu) | _x | 0x7F7F7F7Fu);                    (__builtin_ctz(_x) + 1) >> 3; })
#endif

unsigned char *findFirstByte32(unsigned char *ptr, unsigned char byte) {
  uint32_t *ptr32 = (uint32_t *)ptr, firstByte32 = 0u, byteMask32 = (byte) | (byte << 8);
  byteMask32 |= byteMask32 << 16;
  while((firstByte32 = findFirstZeroByte32((*ptr32) ^ byteMask32)) == 0) { ptr32++; }
  return(ptr + ((((unsigned char *)ptr32) - ptr) + firstByte32 - 1));
}

... และรุ่น 64 บิต:

#ifdef __BIG_ENDIAN__
#define findFirstZeroByte64(x) ({ uint64_t _x = (x); _x = ~(((_x & 0x7F7F7F7F7f7f7f7full) + 0x7F7F7F7F7f7f7f7full) | _x | 0x7F7F7F7F7f7f7f7full); (_x == 0ull) ? 0 : (__builtin_clzll(_x) >> 3) + 1; })
#else
#define findFirstZeroByte64(x) ({ uint64_t _x = (x); _x = ~(((_x & 0x7F7F7F7F7f7f7f7full) + 0x7F7F7F7F7f7f7f7full) | _x | 0x7F7F7F7F7f7f7f7full);                    (__builtin_ctzll(_x) + 1) >> 3; })
#endif

unsigned char *findFirstByte64(unsigned char *ptr, unsigned char byte) {
  uint64_t *ptr64 = (uint64_t *)ptr, firstByte64 = 0u, byteMask64 = (byte) | (byte << 8);
  byteMask64 |= byteMask64 << 16;
  byteMask64 |= byteMask64 << 32;
  while((firstByte64 = findFirstZeroByte64((*ptr64) ^ byteMask64)) == 0) { ptr64++; }
  return(ptr + ((((unsigned char *)ptr64) - ptr) + firstByte64 - 1));
}

แก้ไข 2011/06/04 OP ชี้ให้เห็นในความคิดเห็นว่าวิธีการแก้ปัญหานี้มี "ข้อผิดพลาดผ่านไม่ได้":

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

นี่เป็นความจริงทางเทคนิค แต่ใช้กับอัลกอริทึมใด ๆ ที่ทำงานกับชิ้นส่วนที่มีขนาดใหญ่กว่าหนึ่งไบต์รวมถึงวิธีที่ OP แนะนำโดยความเห็น:

การstrchrใช้งานทั่วไปไม่ได้ไร้เดียงสา แต่ค่อนข้างมีประสิทธิภาพมากกว่าที่คุณให้ ดูจุดสิ้นสุดของสิ่งนี้สำหรับอัลกอริทึมที่ใช้กันอย่างแพร่หลาย: http://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord

นอกจากนี้ยังไม่มีอะไรเกี่ยวข้องกับการจัดตำแหน่งต่อ se จริงสิ่งนี้อาจทำให้เกิดพฤติกรรมที่กล่าวถึงส่วนใหญ่ของสถาปัตยกรรมทั่วไปที่ใช้อยู่ แต่สิ่งนี้มีส่วนเกี่ยวข้องกับรายละเอียดการใช้งาน microarchitecture - มากขึ้นหากการอ่านที่ไม่ได้จัดแนว straddles ขอบเขต 4K (อีกครั้งทั่วไป) จากนั้นการอ่านจะทำให้โปรแกรม การยุติข้อผิดพลาดหากไม่มีการแมปขอบเขต 4K หน้าถัดไป

แต่นี่ไม่ใช่ "บั๊ก" ในอัลกอริทึมที่ให้ไว้ในคำตอบ - พฤติกรรมนั้นเป็นเพราะฟังก์ชั่นชอบstrchrและstrlenไม่ยอมรับlengthอาร์กิวเมนต์ที่จะ จำกัด ขนาดของการค้นหา การค้นหาchar bytes[1] = {0x55};ซึ่งเพื่อจุดประสงค์ในการสนทนาของเราจะเกิดขึ้นที่ส่วนท้ายสุดของขอบเขตหน้า 4K VM และหน้าถัดไปจะไม่ได้รับการแมปด้วยstrchr(bytes, 0xAA)(ซึ่งstrchrการใช้งานแบบไบต์ต่อครั้ง) จะผิดพลาดอย่างแน่นอน วิธีการเดียวกัน. เช่นเดียวกันสำหรับญาติที่เกี่ยวข้องstrchrstrlen

หากไม่มีlengthอาร์กิวเมนต์คุณจะไม่สามารถบอกได้ว่าเมื่อใดที่คุณควรเปลี่ยนจากอัลกอริธึมความเร็วสูงและกลับไปใช้อัลกอริทึมแบบไบต์ต่อไบต์ มากขึ้นแนวโน้ม "ข้อผิดพลาด" จะอ่าน "ที่ผ่านมาขนาดของการจัดสรร" ซึ่งในทางเทคนิคส่งผลในการundefined behaviorตามมาตรฐานภาษา C valgrindต่างๆและจะถูกจัดเป็นข้อผิดพลาดจากสิ่งที่ชอบ

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

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

OP ยังระบุไว้ในความคิดเห็น:

สำหรับการเพิ่มประสิทธิภาพ ctz ของคุณมันสร้างความแตกต่างให้กับการทำงานหาง O (1) เท่านั้น มันสามารถปรับปรุงประสิทธิภาพด้วยสตริงเล็ก ๆ (เช่นstrchr("abc", 'a');แต่ไม่แน่นอนกับสตริงที่มีขนาดใหญ่ ๆ

คำแถลงนี้จะเป็นจริงหรือไม่นั้นขึ้นอยู่กับสถาปัตยกรรมแบบไมโครที่มีปัญหา การใช้โมเดลไปป์ไลน์ canonical 4 stage RISC นั้นเป็นจริงเกือบแน่นอน แต่มันก็ยากที่จะบอกได้ว่ามันเป็นความจริงสำหรับซีพียูซุปเปอร์สเกลาร์นอกสมัยที่ความเร็วแกนกลางสามารถแคระความเร็วการสตรีมหน่วยความจำได้อย่างเต็มที่ ในกรณีนี้ไม่เพียง แต่น่าเชื่อถือเท่านั้น แต่เป็นเรื่องธรรมดาที่จะมีช่องว่างขนาดใหญ่ใน "จำนวนคำสั่งที่สามารถยกเลิกได้" เทียบกับ "จำนวนไบต์ที่สามารถสตรีมได้" เพื่อให้คุณมี จำนวนคำสั่งที่สามารถยกเลิกได้สำหรับแต่ละไบต์ที่สามารถสตรีมได้ " หากนี่มีขนาดใหญ่พอctzคำสั่ง + shift สามารถทำได้ "ฟรี"


"สำหรับเข็มที่มีความยาว 1 ใช้strchr" - คุณถามถึงวิธีค้นหาสตริงย่อยที่เร็วที่สุด การค้นหาซับสตริงที่มีความยาว 1 เป็นเพียงกรณีพิเศษอันหนึ่งที่สามารถปรับให้เหมาะสม หากคุณสลับรหัสกรณีพิเศษปัจจุบันของคุณสำหรับสตริงย่อยที่มีความยาว 1 ( strchr) ด้วยสิ่งต่าง ๆ ข้างต้นสิ่งต่างๆจะ (เร็วขึ้นขึ้นอยู่กับวิธีstrchrการนำไปใช้) จะเร็วขึ้น อัลกอริทึมข้างต้นเกือบ 3x เร็วกว่าการstrchrใช้งานไร้เดียงสาทั่วไป
johne

2
OP กล่าวว่าสายอักขระถูกยกเลิกอย่างถูกต้องเป็นโมฆะดังนั้นการสนทนาของคุณเกี่ยวกับchar bytes[1] = {0x55};ไม่เกี่ยวข้อง ที่เกี่ยวข้องมากคือความคิดเห็นของคุณเกี่ยวกับสิ่งนี้เป็นจริงสำหรับอัลกอริทึมการอ่านคำใด ๆ ที่ไม่ทราบความยาวก่อน
เซทโรเบิร์ตสันส์

1
ปัญหาใช้ไม่ได้กับเวอร์ชันที่ฉันอ้างถึงเพราะคุณใช้เฉพาะกับพอยน์เตอร์ที่ได้รับการจัดตำแหน่ง - อย่างน้อยนั่นก็คือการใช้งานที่ถูกต้อง
. GitHub หยุดช่วยน้ำแข็ง

2
@R ไม่มีส่วนเกี่ยวข้องกับ "ตัวชี้ที่จัดตำแหน่ง" สมมุติฐานถ้าคุณมีสถาปัตยกรรมที่รองรับการป้องกัน VM ด้วยความละเอียดระดับไบต์และการmallocจัดสรรแต่ละรายการมี "เบาะอย่างเพียงพอ" ที่ด้านใดด้านหนึ่งและระบบ VM บังคับใช้การป้องกันแบบเป็นไบต์สำหรับการจัดสรรนั้น .... ไม่ว่าตัวชี้จะถูกจัดแนวหรือไม่ สมมติว่าการintจัดตำแหน่งตามธรรมชาติแบบ 32 บิตเล็กน้อย) เป็นสิ่งที่สงสัย แต่ก็ยังมีความเป็นไปได้ที่การอ่านแบบอ่านแล้วเพื่ออ่านขนาดของการจัดสรร ใด ๆundefined behaviorอ่านที่ผ่านมาขนาดของการจัดสรรคือ
johne

5
@johne: +1 เพื่อแสดงความคิดเห็น แนวความคิดของคุณถูกต้อง แต่ความจริงก็คือการปกป้องไบต์ที่มีราคาแพงทั้งในการจัดเก็บและการบังคับใช้ที่พวกเขาไม่ได้และจะไม่มีอยู่จริง หากคุณทราบว่าที่เก็บข้อมูลพื้นฐานคือการจับคู่หน้าเว็บซึ่งได้จากการเทียบเท่าการmmapจัดตำแหน่งนั้นเพียงพอแล้ว
. GitHub หยุดช่วยเหลือน้ำแข็ง

3

เพียงค้นหา "strstr ที่เร็วที่สุด" และหากคุณเห็นสิ่งที่น่าสนใจเพียงแค่ถามฉัน

ในมุมมองของฉันคุณกำหนดข้อ จำกัด มากเกินไปสำหรับตัวคุณเอง (ใช่เราทุกคนต้องการ sub-linear linear ที่ max searcher) แต่มันต้องใช้โปรแกรมเมอร์ตัวจริงก้าวเข้ามาจนกว่าฉันจะคิดว่าวิธีแฮชนั้นเป็นวิธีที่ดี เสริมอย่างดีโดย BNDM สำหรับรูปแบบที่สั้นกว่า 2..16)

เป็นเพียงตัวอย่างด่วน:

ทำค้นหาแบบ (32bytes) ลงในสตริง (206908949bytes) เป็นหนึ่งเส้น ... ข้ามประสิทธิภาพ (ขนาดใหญ่ที่ดีกว่า): 3041% 6801754 ข้าม / ซ้ำ Railgun_Quadruplet_7Hasherezade_hits / Railgun_Quadruplet_7Hasherezade_clocks: 0/58 Railgun_Quadruplet_7Hasherezadeประสิทธิภาพ: 3483KB / นาฬิกา

ทำค้นหาแบบ (32bytes) ลงในสตริง (206908949bytes) เป็นหนึ่งเส้น ... ข้ามประสิทธิภาพ (ขนาดใหญ่ที่ดีกว่า): 1,554% 13,307,181 ข้าม / Boyer_Moore_Flensburg_hits ซ้ำ / Boyer_Moore_Flensburg_clocks: 0/83 Boyer_Moore_Flensburgประสิทธิภาพ: 2434KB / นาฬิกา

ทำการค้นหารูปแบบ (32bytes) เป็นสตริง (206908949bytes) เป็นหนึ่งบรรทัด ... ข้ามประสิทธิภาพ (ใหญ่กว่าดีกว่า): 129%, 160239051 ข้าม / ซ้ำซ้ำสองทาง Way_hits / สองทาง Way_clocks: 0/816 สอง -ประสิทธิภาพ: 247KB / นาฬิกา

Sanmayce
ขอแสดงความนับถือ


3

The Two-Way ขั้นตอนวิธีการที่คุณพูดถึงในคำถามของคุณ (! ซึ่งโดยวิธีเป็นที่น่าทึ่ง) เมื่อเร็ว ๆ นี้ได้รับการปรับปรุงในการทำงานได้อย่างมีประสิทธิภาพคำสัญลักษณ์ในเวลา: Optimal เต็มไปจับคู่สาย

ฉันไม่ได้อ่านบทความทั้งหมด แต่ดูเหมือนว่าพวกเขาจะใช้คำสั่ง CPU พิเศษใหม่สองสามอัน (รวมอยู่ในเช่น SSE 4.2) เป็น O (1) สำหรับการเรียกร้องความซับซ้อนของเวลาแม้ว่าพวกเขาจะไม่สามารถใช้ได้ จำลองพวกมันในเวลา O (log log w) สำหรับคำ w-bit ที่ฟังดูไม่เลว


3

คุณสามารถนำไปใช้พูดอัลกอริทึมที่แตกต่างกัน 4 แบบ ทุก ๆ M นาที (เพื่อพิจารณาเชิงประจักษ์) เรียกใช้ทั้ง 4 กับข้อมูลจริงในปัจจุบัน สะสมสถิติการรัน N ครั้ง (เช่น TBD) จากนั้นใช้เฉพาะผู้ชนะในนาที M ถัดไป

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


3

ฉันเพิ่งค้นพบเครื่องมือที่ดีในการวัดประสิทธิภาพของ algos ที่มีอยู่หลากหลาย: http://www.dmi.unict.it/~faro/smart/index.php

คุณอาจพบว่ามันมีประโยชน์. นอกจากนี้ถ้าฉันต้องใช้การเรียกค้นอย่างรวดเร็วเกี่ยวกับอัลกอริทึมการค้นหาย่อยฉันจะไปกับ Knuth-Morris-Pratt


ขอบคุณสำหรับลิงค์ การทดสอบดูน่าสนใจสำหรับช่วงเวลาของกรณีทั่วไป แต่ไม่ใช่สำหรับช่วงเวลาที่เลวร้ายที่สุด
.. GitHub หยุดช่วยน้ำแข็ง

2

คุณอาจต้องการมีการวัดประสิทธิภาพที่หลากหลายด้วยสตริงหลายประเภทเนื่องจากอาจมีผลกระทบอย่างมากต่อประสิทธิภาพ algos จะดำเนินการแตกต่างกันขึ้นอยู่กับการค้นหาภาษาธรรมชาติ (และแม้ที่นี่ยังอาจมีความแตกต่างที่ละเอียดเนื่องจากลักษณะทางสัณฐานวิทยาที่แตกต่างกัน), สาย DNA หรือสตริงสุ่มเป็นต้น

ขนาดตัวอักษรจะมีบทบาทใน algos มากมายเช่นเดียวกับขนาดเข็ม ตัวอย่างเช่น Horspool ทำได้ดีในข้อความภาษาอังกฤษ แต่ไม่ดีกับ DNA เนื่องจากขนาดตัวอักษรที่แตกต่างกันทำให้ชีวิตยากขึ้นสำหรับกฎที่ไม่ดี ขอแนะนำให้ใช้คำต่อท้ายที่ดีเพื่อแก้ไขปัญหานี้อย่างมาก


0

ผมไม่ทราบว่าเป็นที่ดีที่สุดแน่นอน แต่ผมเคยมีประสบการณ์ที่ดีกับบอยเยอร์มัวร์


คุณรู้วิธีการรวมตารางการเปลี่ยนแปลงที่ไม่ดีของ Boyer-Moore กับ Two-Way หรือไม่? Glibc ทำสิ่งนี้แตกต่างสำหรับเข็มยาว (> 32 ไบต์) แต่จะตรวจสอบไบต์สุดท้ายเท่านั้น ปัญหาคือ Two-Way ต้องการค้นหาส่วนที่ถูกต้องของเข็มจากซ้ายไปขวาในขณะที่การเปลี่ยนแปลงที่ไม่ดีของ Boyer-Moore มีประสิทธิภาพมากที่สุดเมื่อค้นหาจากขวาไปซ้าย ฉันพยายามใช้มันด้วยซ้ายไปขวาใน Two-Way (เลื่อนไปข้างหน้าโดยเลื่อนตารางหรือปกติไม่ตรงกันครึ่งทางสองทางแล้วแต่จำนวนใดอีกต่อไป) แต่ฉันช้าลง 5-10% เมื่อเทียบกับ Two-Way ปกติในกรณีส่วนใหญ่และ ไม่พบกรณีใด ๆ ที่ปรับปรุงประสิทธิภาพ
.. GitHub หยุดช่วย ICE

0

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


0

เร็วที่สุดคือ EPSM ในปัจจุบันโดย S. Faro และ OM Kulekci ดูที่http://www.dmi.unict.it/~faro/smart/algorithms.php?algorithm=EPSM&code=epsm

"การจับคู่สตริงที่แน่นอน" ปรับให้เหมาะสมสำหรับ SIMD SSE4.2 (x86_64 และ aarch64) มันมีเสถียรภาพและดีที่สุดในทุกขนาด

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

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