อัลกอริทึมแบบสุ่ม
จากวิกิพีเดีย สารานุกรมเสรี
อัลกอริทึมแบบสุ่ม (randomized algorithm) เป็นอัลกอริทึมที่ยอมให้มีการโยนเหรียญได้ ในทางปฏิบัติ เครื่องที่ใช้ทำงานอัลกอริทึมนี้ จะต้องใช้ตัวสร้างเลขสุ่มเทียม (pseudo-random number generator) ในการสร้างตัวเลขสุ่มขึ้นมา อัลกอรึทึมโดยทั่วๆไปมักใช้บิทสุ่ม (random bit) สำหรับเป็นอินพุตเสริม เพื่อชี้นำการกระทำของมันต่อไป โดยมีความหวังว่าจะช่วยให้มีประสิทธิภาพที่ดีใน "กรณีส่วนมาก(average case)" หรือหากพูดในทางคณิตศาสตร์ก็คือ ประสิทธิภาพของอัลกอริทึมมีค่าเท่ากับตัวแปรสุ่ม (random variable) ซึ่งคำนวณจากบิทสุ่ม โดยหวังว่าจะมีค่าคาดหวัง (expected value) ที่ดี กรณีที่แย่มากที่สุดมักจะมีโอกาสเกิดขึ้นน้อยมากจนแทบจะไม่ต้องสนใจ
หากลองพิจารณาการหาตัวอักษร a ในอาร์เรย์ขนาด n เมื่อสมมุติว่าครึ่งหนึ่งในอาร์เรย์นี้เป็น a และอีกครึ่งหนึ่งเป็น b วิธีที่เห็นชัดวิธีหนึ่งคือการดูแต่ละตัวในอาร์เรย์ แต่วิธีนี้อาจทำให้ต้องดูถึง n/2 ตัวในกรณีที่แย่ที่สุด (นั่นคือครึ่งแรกของอาร์เรย์เป็น b ทั้งหมด) การพยายามแก้ไขเหตุการณ์นี้โดยเปลี่ยนลำดับการดู เช่น อ่านจากหลังมาหน้า หรืออ่านตัวเว้นตัว ก็ไม่ได้ช่วยให้อะไรดีขึ้นเลย ที่จริงแล้ว วิธีการใดก็ตามที่ลำดับของการตรวจสอบสมาชิกแต่ละตัวถูกกำหนดไว้ตายตัวแล้ว (นั่นคือ เป็นอัลกอริทึมดิเทอร์มินิสติก) เราจะไม่สามารถรับประกันได้เลยว่าอัลกอริทึมจะทำงานสำเร็จอย่างรวดเร็ว ในทุกๆอินพุทที่เป็นไปได้ แต่ถ้าหากเราตรวจสอบสมาชิกในอาร์เรย์แบบสุ่ม(ไม่มีลำดับที่แน่นอน) มีความน่าจะเป็นสูงที่เราจะสามารถหา a พบในเวลาอันรวดเร็ว ไม่ว่าอินพุทจะเป็นเช่นไรก็ตาม
ลองจินตนาการว่าเราจะต้องเผชิญหน้ากับ "ผู้ประสงค์ร้าย" ที่เก่งกาจอย่างคาดไม่ถึง กล่าวคือ คนๆนี้สามารถล่วงรู้วิธีการในการจัดการกับปัญหาของอัลกอริทึม และสามารถหาอินพุทที่แย่ที่สุดมาทำให้อัลกอริทึมทำงานได้ประสิทธิภาพไม่ดีได้เสมอ (ดูการวิเคราะห์เชิงแข่งขัน) อย่างไรก็ตามผู้ประสงค์ร้ายคนนี้จะสามารถทำร้ายอัลกอริทึมของเราได้น้อยลง หากอัลกอริทึมไม่ได้มีพฤติกรรมที่แน่นอน (ทำให้ผู้ประสงค์ร้ายไม่สามารถเดาได้ถูก) ด้วยเหตุผลเดียวกันนี้เองที่ทำให้ การสุ่ม เป็นที่แพร่หลายในวิทยาการเข้ารหัสลับ ในงานประยุกต์ทางด้านการเข้ารหัสลับนั้น ตัวเลขสุ่มเทียมไม่สามารถนำมาใช้ได้ เนื่องจากผู้ประสงค์ร้ายสามารถทายเลขนี้ได้ ทำให้อัลกอริทึมมีลักษณะเป็นแบบดิเทอร์มินิสติกดีๆเท่านั้นเอง ดังนั้นจึงจำเป็นต้องมีแหล่งกำเนิดที่สามารถสร้างเลขสุ่มที่แท้จริงได้ หรือไม่ก็ต้องมีตัวสร้างตัวเลขสุ่มเทียมที่มีความปลอดภัยในการเข้ารหัสลับ อีกศาสตร์หนึ่งที่การสุ่มได้หยั่งรากฝังลึกเข้าไปคือคอมพิวเตอร์ควอนตัม (quantum computer)
ในตัวอย่างที่กล่าวมานี้ อัลกอริทึมแบบสุ่มให้ผลลัพธ์ที่ถูกต้องเสมอ เพียงแต่ว่ามีความเป็นไปได้อยู่บ้าง ที่อัลกอริทึมจะใช้เวลานานในการทำงาน บางครั้งเราอาจต้องการอัลกอริทึมที่ทำงานได้เร็วในทุกๆสถานการณ์ แต่เราก็ต้องแลกด้วยโอกาสเกิดความผิดพลาด อัลกอริทึมประเภทแรก(ถูกต้องเสมอ แต่อาจใช้เวลานาน)เรียกว่าอัลกอริทึมลาสเวกัส และแบบหลัง(ต้องทำงานเร็ว แต่มีข้อผิดพลาดได้)เรียกว่าอัลกอริทึมมอนติคาร์โล (ตามชื่อของวิธีมอนติคาร์โลที่ใช้ในการจำลอง (simulation)) สังเกตว่าอัลกอริทึมลาสเวกัสทุกอันสามารถแปลงเป็นอัลกอริทึมมอนติคาร์โลได้ โดยการตอบออกไปมั่วๆ หากไม่สามารถหาคำตอบได้ในเวลาที่กำหนด
ทฤษฎีความซับซ้อนในการคำนวณซึ่งเป็นการศึกษาเกี่ยวกับทรัพยากรทางการคำนวณที่ต้องใช้ในการแก้ปัญหาหนึ่งๆ ได้สร้างแบบจำลองของอัลกอริทึมแบบสุ่มให้เป็นเครื่องจักรทัวริงเชิงความน่าจะเป็น ทั้งอัลกอริทึมลาสเวกัสและมอนติคาร์โลได้ถูกนำมาพิจารณา รวมถึง "คลาสของความซับซ้อน" หลายๆคลาสก็ได้ถูกนำมาศึกษา คลาสของความซับซ้อนแบบสุ่มแบบที่เป็นพื้นฐานที่สุดคือแบบอาร์พี ซึ่งเป็นคลาสของปัญหาการตัดสินใจที่มีอัลกอริทึมแบบสุ่ม (หรือเครื่องจักรทัวริงเชิงความน่าจะเป็น) ที่มีประสิทธิภาพ (ทำงานได้ได้ในเวลาโพลิโนเมียล) ที่สามารถตอบว่า "ไม่" ได้ถูกต้องเสมอ และสามารถตอบว่า "ใช่" ได้ โดยมีโอกาสถูกต้องอย่างน้อย 1/2 คลาสส่วนกลับ (complement) ได้แก่โค-อาร์พี และคลาสของปัญหาซึ่งทั้งคำตอบ "ใช่" และ "ไม่" สามารถมีค่าความน่าจะเป็นได้ทั้งคู่ (นั่นคือ ไม่ได้บังคับให้ต้องตอบถูกต้องเสมอ) เรียกว่าซีพีพี(ZPP) สำหรับปัญหาซึ่ง(เชื่อกันว่า)อยู่นอกคลาสนี้ เช่นปัญหาเอ็นพีแบบยาก (ซึ่งแม้แต่อัลกอริทึมแบบสุ่มก็ไม่สามารถแก้ได้) จำเป็นต้องแก้ด้วยอัลกอริทึมแบบประมาณ
ในประวัติศาสตร์ อัลกอริทึมแบบสุ่มเริ่มเป็นที่รู้จัก จากการค้นพบของมิลเลอร์และราบินในปี ค.ศ. 1976 ว่า ปัญหาการตรวจสอบการเป็นจำนวนเฉพาะของตัวเลข สามารถแก้ได้อย่างมีประสิทธิภาพด้วยอัลกอริทึมแบบสุ่ม ในเวลานั้น ยังไม่มีอัลกอริทึมดิเทอร์มินิสติกสำหรับปัญหานี้เลย
การตรวจสอบการเป็นจำนวนเฉพาะมิลเลอร์-ราบินนั้น มีหลักการพื้นฐานอยู่บนความสัมพันธ์ทวิภาค ระหว่างจำนวนเต็มบวกสองตัว k และ n ใดๆ ที่สามารถบอกได้ว่า k "เป็นตัวยืนยันการเป็นจำนวนประกอบของ" n เราสามารถแสดงได้ว่า
- ถ้ามีตัวยืนยันการเป็นจำนวนประกอบของ n แล้ว n เป็นจำนวนประกอบ (หมายความว่า n ไม่เป็นจำนวนเฉพาะ) และ
- ถ้า n เป็นจำนวนประกอบแล้ว มีอย่างน้อยสามในสี่ของจำนวนนับที่มีค่าน้อยกว่า n ที่เป็นตัวยืนยันการเป็นจำนวนประกอบของ n ได้ และ
- มีอัลกอริทึมที่ทำงานได้รวดเร็วพอ ที่เมื่อให้ค่า k และค่า n อัลกอริทึมสามารถบอกได้ว่า k เป็นตัวยืนยันการเป็นจำนวนประกอบของ n หรือไม่
สังเกตว่าข้อเท็จจริงเหล่านี้ทำให้สรุปได้ว่าปัญหาการทดสอบการเป็นจำนวนเฉพาะอยู่ในโค-อาร์พี สมมุติ n เป็นจำนวนประกอบ ถ้าเราเลือกตัวเลขที่น้อยกว่า n มี 100 ตัว ความน่าจะเป็นที่จะหา "ตัวยืนยัน" ดังกล่าวไม่เจอจะเป็น (1/4)100 ซึ่งในทางปฏิบัติแล้ววิธีนี้ก็เป็นการทดสอบการเป็นจำนวนเฉพาะที่ใช้ได้วิธีหนึ่ง และอาจจะไม่มีวิธีใดเลยที่ใช้ได้ดีในทางปฏิบัติเมื่อ n มีขนาดใหญ่มาก เราสามารถลดความน่าจะเป็นที่จะเกิดความผิดพลาดให้เหลือเท่าใดก็ได้ โดยการเพิ่มรอบการทำงานให้มากพอ
ดังนั้น ในทางปฏิบัติแล้ว จึงมักไม่ค่อยมีใครสนใจกับโอกาสเกิดความผิดพลาดที่มีเล็กน้อยนี้สักเท่าไร เพราะเราสามารถทำให้มันน้อยลงเท่าไรก็ได้ตามใจปรารถนา ที่จริงแล้ว ถึงแม้ว่าเราจะมีอัลกอริทึมในการตรวจสอบการเป็นจำนวนเฉพาะแบบดิเทอร์มินิสติกที่สามารถทำงานได้ในเวลาโพลิโนเมียลแล้ว มันก็ยังไม่ได้ถูกนำไปใช้แทนวิธีเชิงความน่าจะเป็นแบบเดิมในซอฟต์แวร์ด้านการเข้ารหัสลับ และก็ยังไม่มีใครคิดว่าจะเป็นเช่นนั้นได้ในอนาคตอันใกล้นี้ด้วย
สมมุติว่าเราใช้วิธีเชิงสุ่ม แล้วมีความน่าจะเป็นที่จะเกิดความผิดพลาดเป็น 2−1000 คำถามที่ตามมาคือ ตัวเลขนี้เกิดจากการพิสูจน์ทางคณิตศาสตร์หรือไม่? ถึงแม้ว่าโอกาสผิดพลาดจะน้อยมากเมื่อเทียบกับโอกาสเกิดความผิดพลาดของฮาร์ดแวร์ที่ใช้ทำมัน หรือโอกาสที่คนตรวจบทพิสูจน์จะมองข้ามความผิดพลาดไป แต่จริงๆแล้วการบอกว่ามีความน่าจะเป็นน้อยนี้ ควรให้ความหมายว่าอย่างไรดี?
ควิกซอร์ต น่าจะเป็นอัลกอริทึมที่ใช้จริงที่เราคุ้นเคยที่สุดที่ใช้การสุ่มอย่างได้ผลดีมากๆ อัลกอริทึมนี้ในแบบที่เป็นดิเทอร์มินิสติกต้องใช้เวลา O(n^2) ในการเรียงเลข n ตัว สำหรับอินพุทบางรูปแบบ เช่น อาร์เรย์ที่ถูกเรียงมาอยู่แล้ว อย่างไรก็ตาม ถ้าอัลกอริทึมสลับตัวในอาร์เรย์แบบสุ่มก่อนที่จะเริ่มทำงาน มันจะมีความน่าจะเป็นสูงที่จะทำงานเสร็จในเวลา O(n log n) สำหรับอินพุททุกรูปแบบ ความแตกต่างระหว่างสองแบบนี้จะมีความสำคัญมาก เมื่อเราต้องจัดเรียงข้อมูลจำนวนมากๆ
ตัวอย่างที่ซับซ้อนขึ้นกว่าอีกหน่อย คือการใช้อัลกอริทึมเชิงสุ่มแก้ปัญหาทางด้านทฤษฎีกราฟ นี่คืออัลกอริทึมสำหรับแก้ปัญหา การตัดให้น้อยที่สุด(minimum cut)
หาการตัดน้อยสุด(กราฟไม่มีทิศทาง G) { ตราบใดที่ G ยังมีโหนดมากกว่า 2 โหนด ให้ทำ{ สุ่มเลือกเส้นเชื่อม (u,v) จาก G หด (contract) เส้นเชื่อม โดยให้รักษาการมีเส้นเชื่อมหลายเส้น (multi-edge)เอาไว้ ลบลูปทั้งหมดออก } ให้เส้นเชื่อมที่เหลืออยู่เป็นเอาท์พุท }
ในที่นี้ การหดเส้นเชื่อม (u,v) หมายความถึง การเพิ่มโหนดขึ้นใหม่ (ให้ชื่อ w) แล้วให้แทนทุกเส้นเชื่อม (u,x) และ (v,x) ด้วย (w,x) แล้วลบโหนด u และ v ออกจาก G
ให้ n = |V[G]| สามารถแสดงได้ว่า อัลกอริทึมนี้ให้ผลเป็นการตัดที่น้อยที่สุด ด้วยความน่าจะเป็นอย่างน้อย n-2 ดังนั้นหากเราให้มันทำงาน n2log(n) รอบ และเลือกผลลัพธ์ที่มีค่าน้อยที่สุด เราก็จะสามารถหาการตัดที่น้อยที่สุดได้ด้วยความน่าจะเป็นสูง
[แก้] อ้างอิง
- R. Motwani and P. Raghavan. Randomized Algorithms. Cambridge University Press, New York (NY), 1995.
- M. Mitzenmacher and E. Upfal. Probability and Computing : Randomized Algorithms and Probabilistic Analysis. Cambridge University Press, New York (NY), 2005.