ประสิทธิภาพของอัลกอริทึมเป็นคุณสมบัติของอัลกอริทึมที่เกี่ยวข้องกับทรัพยากรการคำนวณที่ใช้โดยอัลกอริทึม อัลกอริทึมจะต้องได้รับการวิเคราะห์เพื่อกำหนดทรัพยากรที่อัลกอริทึมต้องการ ประสิทธิภาพของอัลกอริทึมสามารถมองได้ว่าคล้ายคลึงกับประสิทธิภาพการผลิตของกระบวนการที่ทำซ้ำหรือต่อเนื่องกัน
เพื่อให้บรรลุประสิทธิภาพสูงสุด เราต้องการลดการใช้ทรัพยากร อย่างไรก็ตาม ไม่สามารถเปรียบเทียบทรัพยากรที่แตกต่างกัน (เช่น เวลาและหน่วยความจำ) ได้โดยตรง ดังนั้นอัลกอริธึมใดในสองอัลกอริธึมที่ถือว่ามีประสิทธิภาพมากกว่ามักจะขึ้นอยู่กับปัจจัยที่มีความสำคัญมากกว่า เช่น ข้อกำหนดสำหรับความเร็วสูง การใช้หน่วยความจำขั้นต่ำ หรือการวัดอื่น ๆ ประสิทธิภาพ.
โปรดทราบว่าบทความนี้ ไม่เกี่ยวกับการเพิ่มประสิทธิภาพอัลกอริทึมซึ่งจะกล่าวถึงในบทความ การเพิ่มประสิทธิภาพโปรแกรม, การเพิ่มประสิทธิภาพคอมไพเลอร์, การเพิ่มประสิทธิภาพวงจร, เครื่องมือเพิ่มประสิทธิภาพรหัสวัตถุและอื่นๆ คำว่า "การเพิ่มประสิทธิภาพ" เองนั้นทำให้เข้าใจผิด เพราะทุกสิ่งที่สามารถทำได้ตกอยู่ภายใต้ร่มของ "การปรับปรุง"พื้นหลัง
ความสำคัญของประสิทธิภาพโดยเน้นที่เวลาดำเนินการได้รับการเน้นย้ำโดย Ada Lovelace ในปี 1843 เกี่ยวกับเครื่องมือวิเคราะห์ทางกลของ Charles Babbage:
“ในการประมวลผลเกือบทั้งหมด มีการกำหนดค่าให้เลือกมากมายเพื่อให้กระบวนการเสร็จสมบูรณ์ และแบบแผนที่แตกต่างกันควรมีอิทธิพลต่อตัวเลือกสำหรับวัตถุประสงค์ในการดำเนินการคำนวณ สิ่งสำคัญคือการเลือกการกำหนดค่าที่จะส่งผลให้ใช้เวลาในการคำนวณน้อยที่สุด"
คอมพิวเตอร์อิเล็กทรอนิกส์ในยุคแรกๆ นั้นมีข้อจำกัดอย่างมากทั้งในด้านความเร็วและหน่วยความจำ ในบางกรณี เป็นที่ทราบกันว่ามีการแลกเปลี่ยนหน่วยความจำเวลา ซึ่งงานต้องใช้หน่วยความจำจำนวนมากเพื่อให้ได้ความเร็วสูง หรือใช้อัลกอริธึมที่ช้ากว่าซึ่งใช้หน่วยความจำในการทำงานจำนวนเล็กน้อย ในกรณีนี้ มีการใช้อัลกอริธึมที่เร็วที่สุดซึ่งมีหน่วยความจำเพียงพอ
คอมพิวเตอร์ยุคใหม่เร็วกว่าคอมพิวเตอร์ยุคแรกๆ มากและมีหน่วยความจำมากกว่ามาก (กิกะไบต์แทนที่จะเป็นกิโลไบต์) อย่างไรก็ตาม Donald Knuth เน้นย้ำว่าประสิทธิภาพยังคงเป็นปัจจัยสำคัญ:
“ในสาขาวิชาวิศวกรรมที่จัดตั้งขึ้น การปรับปรุง 12% สามารถทำได้ง่ายและไม่เคยถูกมองว่าเป็นการห้าม และฉันเชื่อว่าสิ่งเดียวกันนี้ควรเป็นจริงในการเขียนโปรแกรม”
ทบทวน
อัลกอริทึมจะถือว่ามีประสิทธิภาพหากการใช้ทรัพยากร (หรือต้นทุนทรัพยากร) อยู่ที่หรือต่ำกว่าระดับที่ยอมรับได้ หากพูดโดยคร่าวๆ แล้ว "ยอมรับได้" ในที่นี้หมายถึง "อัลกอริทึมจะทำงานเป็นระยะเวลาพอสมควรบนคอมพิวเตอร์ที่มีอยู่" เนื่องจากพลังการประมวลผลและหน่วยความจำที่มีอยู่ของคอมพิวเตอร์เพิ่มขึ้นอย่างมากนับตั้งแต่ทศวรรษ 1950 "ระดับที่ยอมรับได้" ในปัจจุบันจึงไม่เป็นที่ยอมรับแม้แต่ 10 ปีที่แล้ว
ผู้ผลิตคอมพิวเตอร์จะออกรุ่นใหม่เป็นระยะซึ่งมักจะมีประสิทธิภาพมากกว่า ค่าใช้จ่ายของซอฟต์แวร์อาจค่อนข้างสูง ดังนั้นในบางกรณี จึงง่ายกว่าและถูกกว่าเพื่อให้ได้ประสิทธิภาพที่ดีขึ้นโดยการซื้อคอมพิวเตอร์ที่เร็วกว่าซึ่งเข้ากันได้กับคอมพิวเตอร์ที่คุณมีอยู่
มีหลายวิธีในการวัดทรัพยากรที่ใช้โดยอัลกอริทึม การวัดที่ใช้มากที่สุดสองรายการคือความเร็วและหน่วยความจำที่ใช้ การวัดอื่นๆ อาจรวมถึงความเร็วการถ่ายโอน การใช้ดิสก์ชั่วคราว การใช้ดิสก์ในระยะยาว การใช้พลังงาน ต้นทุนการเป็นเจ้าของทั้งหมด เวลาตอบสนองต่อสัญญาณภายนอก และอื่นๆ การวัดจำนวนมากเหล่านี้ขึ้นอยู่กับขนาดของข้อมูลอินพุตของอัลกอริทึม (นั่นคือ ปริมาณที่ต้องใช้การประมวลผลข้อมูล) การวัดยังอาจขึ้นอยู่กับวิธีการนำเสนอข้อมูล (เช่น อัลกอริธึมการเรียงลำดับบางอย่างทำงานได้ไม่ดีกับข้อมูลที่เรียงลำดับแล้ว หรือเมื่อข้อมูลถูกจัดเรียงในลำดับย้อนกลับ)
ในทางปฏิบัติ ยังมีปัจจัยอื่นๆ ที่มีอิทธิพลต่อประสิทธิภาพของอัลกอริทึม เช่น ความแม่นยำและ/หรือความน่าเชื่อถือที่ต้องการ ตามที่อธิบายไว้ด้านล่าง วิธีการนำอัลกอริทึมไปใช้ยังสามารถส่งผลกระทบอย่างมีนัยสำคัญต่อประสิทธิภาพที่แท้จริง แม้ว่าการใช้งานหลายแง่มุมจะเป็นปัญหาในการเพิ่มประสิทธิภาพก็ตาม
การวิเคราะห์เชิงทฤษฎี
ในการวิเคราะห์ทางทฤษฎีของอัลกอริธึม เป็นเรื่องปกติที่จะประมาณความซับซ้อนของอัลกอริธึมในพฤติกรรมเชิงเส้นกำกับของมัน กล่าวคือ เพื่อสะท้อนความซับซ้อนของอัลกอริธึมเป็นฟังก์ชันของขนาดของอินพุต nใช้สัญลักษณ์ O ใหญ่ โดยทั่วไปการประมาณนี้ค่อนข้างแม่นยำสำหรับขนาดใหญ่ nแต่อาจนำไปสู่ข้อสรุปที่ไม่ถูกต้องด้วยค่าที่น้อย n(ดังนั้น การจัดเรียงแบบบับเบิลซึ่งถือว่าช้า อาจเร็วกว่าการเรียงลำดับด่วน หากคุณต้องการจัดเรียงองค์ประกอบเพียงไม่กี่รายการเท่านั้น)
การกำหนด | ชื่อ | ตัวอย่าง |
---|---|---|
O(1) (\รูปแบบการแสดงผล O(1)\,) | ถาวร | การกำหนดว่าตัวเลขเป็นคู่หรือคี่ การใช้ตารางค้นหาขนาดคงที่ การใช้ฟังก์ชันแฮชที่เหมาะสมเพื่อเลือกองค์ประกอบ |
O (log n) (\displaystyle O(\log n)\,) | ลอการิทึม | การค้นหาองค์ประกอบในอาร์เรย์ที่เรียงลำดับโดยใช้การค้นหาแบบไบนารีหรือทรีที่สมดุล คล้ายกับการดำเนินการกับฮีปทวินาม |
O(n) (\รูปแบบการแสดงผล O(n)\,) | เชิงเส้น | การค้นหาองค์ประกอบในรายการที่ไม่เรียงลำดับหรือแผนผังที่ไม่สมดุล (กรณีที่แย่ที่สุด) บวกสอง n- บิตตัวเลขโดยใช้การพกพาจากต้นทางถึงปลายทาง |
O (n log n) (\displaystyle O(n\log n)\,) | กึ่งเชิงเส้น เชิงเส้นลอการิทึม | คำนวณการแปลงฟูเรียร์ที่รวดเร็ว, การเรียงลำดับฮีป, การเรียงลำดับอย่างรวดเร็ว (กรณีที่ดีที่สุดและโดยเฉลี่ย), การเรียงลำดับแบบผสาน |
O (n 2) (\displaystyle O(n^(2))\,) | สี่เหลี่ยม | คูณสอง n-ตัวเลขหลักโดยใช้อัลกอริธึมอย่างง่าย, การเรียงลำดับแบบฟอง (กรณีที่แย่ที่สุด), การเรียงลำดับเชลล์, การเรียงลำดับแบบด่วน (กรณีที่แย่ที่สุด), การเรียงลำดับการเลือก, การเรียงลำดับการแทรก |
O (c n) , c > 1 (\displaystyle O(c^(n)),\;c>1) | เอ็กซ์โปเนนเชียล | การค้นหาวิธีแก้ปัญหา (ที่แน่นอน) สำหรับปัญหาพนักงานขายที่กำลังเดินทางโดยใช้การเขียนโปรแกรมแบบไดนามิก การพิจารณาว่าข้อความสั่งเชิงตรรกะสองข้อความเทียบเท่ากันหรือไม่โดยใช้การค้นหาอย่างละเอียด |
การทดสอบเพื่อยืนยัน: การวัดประสิทธิภาพ
สำหรับซอฟต์แวร์เวอร์ชันใหม่หรือเพื่อทำการเปรียบเทียบกับระบบของคู่แข่ง บางครั้งการวัดประสิทธิภาพจะใช้เพื่อเปรียบเทียบประสิทธิภาพสัมพัทธ์ของอัลกอริธึม ตัวอย่างเช่น หากมีการเปิดตัวอัลกอริธึมการเรียงลำดับใหม่ ก็สามารถเปรียบเทียบกับรุ่นก่อนๆ ได้ เพื่อให้แน่ใจว่าอัลกอริธึมจะมีประสิทธิภาพกับข้อมูลที่รู้จักเป็นอย่างน้อยเท่ากับข้อมูลอื่นๆ ผู้ใช้สามารถใช้การทดสอบประสิทธิภาพเพื่อเปรียบเทียบผลิตภัณฑ์จากผู้ผลิตหลายรายเพื่อประเมินว่าผลิตภัณฑ์ใดจะเหมาะสมกับความต้องการของตนมากที่สุดในแง่ของฟังก์ชันการทำงานและประสิทธิภาพ
การทดสอบเกณฑ์มาตรฐานบางรายการให้การวิเคราะห์เปรียบเทียบของการคอมไพล์และการตีความภาษาต่างๆ เช่น PC Benchmark Collection ของ Roy Longbottom และ เกมเกณฑ์มาตรฐานภาษาคอมพิวเตอร์เปรียบเทียบประสิทธิภาพของการใช้งานทั่วไปในภาษาการเขียนโปรแกรมบางภาษา
ปัญหาการดำเนินงาน
ปัญหาการใช้งานอาจส่งผลต่อประสิทธิภาพการทำงานจริงด้วย ซึ่งรวมถึงการเลือกภาษาการเขียนโปรแกรมและวิธีการเข้ารหัสอัลกอริทึม ตัวเลือกนักแปลสำหรับภาษาที่เลือกหรือตัวเลือกคอมไพเลอร์ที่ใช้ และแม้แต่ระบบปฏิบัติการที่ใช้ ในบางกรณี ภาษาที่ใช้เป็นล่ามอาจช้ากว่าภาษาที่ใช้เป็นคอมไพเลอร์อย่างมาก
มีปัจจัยอื่น ๆ ที่อาจส่งผลต่อเวลาหรือการใช้หน่วยความจำที่อยู่นอกเหนือการควบคุมของโปรแกรมเมอร์ ซึ่งรวมถึงการจัดตำแหน่งข้อมูล รายละเอียดการรวบรวมขยะ ความเท่าเทียมระดับคำสั่งและการเรียกรูทีนย่อย
โปรเซสเซอร์บางตัวมีความสามารถในการดำเนินการเวกเตอร์ ซึ่งช่วยให้การดำเนินการหนึ่งสามารถประมวลผลตัวถูกดำเนินการหลายตัวได้ การใช้คุณสมบัติดังกล่าวในระดับการเขียนโปรแกรมหรือการคอมไพล์อาจหรืออาจไม่ง่ายเลย อัลกอริธึมที่ออกแบบมาสำหรับการประมวลผลตามลำดับอาจต้องมีการออกแบบใหม่ทั้งหมดเพื่อรองรับการประมวลผลแบบขนาน
ปัญหาอื่นอาจเกิดขึ้นกับความเข้ากันได้ของโปรเซสเซอร์ ซึ่งคำแนะนำอาจแตกต่างออกไป ดังนั้นคำแนะนำในบางรุ่นอาจค่อนข้างช้ากว่าในรุ่นอื่นๆ นี่อาจเป็นปัญหาสำหรับคอมไพเลอร์ที่ปรับให้เหมาะสม
การวัดการใช้ทรัพยากร
การวัดมักจะแสดงเป็นฟังก์ชันของขนาดของทางเข้า n.
สองมิติที่สำคัญที่สุดคือ:
- เวลา: อัลกอริธึมใช้เวลานานเท่าใดใน CPU
- หน่วยความจำ: ต้องใช้หน่วยความจำในการทำงานจำนวนเท่าใด (โดยปกติคือ RAM) สำหรับอัลกอริทึม มีสองด้านดังนี้: จำนวนหน่วยความจำสำหรับโค้ดและจำนวนหน่วยความจำสำหรับข้อมูลที่โค้ดทำงาน
สำหรับคอมพิวเตอร์ที่ใช้พลังงานแบตเตอรี่ (เช่น แล็ปท็อป) หรือสำหรับการคำนวณที่ยาว/มาก (เช่น ซูเปอร์คอมพิวเตอร์) การวัดประเภทอื่นที่น่าสนใจ:
- การใช้พลังงานโดยตรง: พลังงานที่จำเป็นในการใช้งานคอมพิวเตอร์
- การใช้พลังงานทางอ้อม: พลังงานที่จำเป็นสำหรับการทำความเย็น แสงสว่าง ฯลฯ
ในบางกรณี จำเป็นต้องมีการวัดอื่นๆ ที่ใช้กันน้อยกว่า:
- ขนาดเกียร์: แบนด์วิธอาจเป็นปัจจัยจำกัด การบีบอัดสามารถใช้เพื่อลดจำนวนข้อมูลที่ถ่ายโอนได้ การแสดงรูปภาพ (เช่น โลโก้ Google) อาจส่งผลให้มีการถ่ายโอนข้อมูลนับหมื่นไบต์ (ในกรณีนี้คือ 48K) เปรียบเทียบสิ่งนี้กับการส่งหกไบต์ในคำว่า "Google"
- หน่วยความจำภายนอก: หน่วยความจำที่ต้องการบนดิสก์หรืออุปกรณ์จัดเก็บข้อมูลภายนอกอื่น ๆ หน่วยความจำนี้สามารถใช้เป็นที่จัดเก็บชั่วคราวหรือใช้ในอนาคตได้
- เวลาตอบสนอง: การตั้งค่านี้มีความสำคัญอย่างยิ่งสำหรับแอปพลิเคชันแบบเรียลไทม์ที่คอมพิวเตอร์ต้องตอบสนองต่อเหตุการณ์ภายนอกอย่างรวดเร็ว
- ต้นทุนการเป็นเจ้าของทั้งหมด: พารามิเตอร์มีความสำคัญเมื่อมีวัตถุประสงค์เพื่อดำเนินการอัลกอริธึมเดียว
เวลา
ทฤษฎี
การทดสอบประเภทนี้ยังขึ้นอยู่กับการเลือกภาษาการเขียนโปรแกรม คอมไพเลอร์ และตัวเลือกของมันอย่างมาก ดังนั้นอัลกอริธึมที่เปรียบเทียบจะต้องถูกนำมาใช้ภายใต้เงื่อนไขเดียวกัน
หน่วยความจำ
ส่วนนี้เกี่ยวข้องกับการใช้หน่วยความจำหลัก (มักเป็น RAM) ที่จำเป็นสำหรับอัลกอริทึม เช่นเดียวกับการวิเคราะห์เวลาด้านบน การวิเคราะห์อัลกอริทึมมักจะใช้ ความซับซ้อนเชิงพื้นที่ของอัลกอริทึมเพื่อประมาณค่าหน่วยความจำรันไทม์ที่ต้องการตามฟังก์ชันของขนาดอินพุต ผลลัพธ์มักจะแสดงเป็น "O" ใหญ่
การใช้หน่วยความจำมีสี่ด้าน:
- จำนวนหน่วยความจำที่ต้องใช้ในการจัดเก็บโค้ดอัลกอริทึม
- จำนวนหน่วยความจำที่จำเป็นสำหรับข้อมูลอินพุต
- จำนวนหน่วยความจำที่จำเป็นสำหรับเอาต์พุตใดๆ (อัลกอริธึมบางอย่าง เช่น การเรียงลำดับ มักจะจัดเรียงอินพุตใหม่ และไม่ต้องการหน่วยความจำเพิ่มเติมสำหรับเอาต์พุต)
- จำนวนหน่วยความจำที่กระบวนการคำนวณต้องการระหว่างการคำนวณ (ซึ่งรวมถึงตัวแปรที่มีชื่อและพื้นที่สแต็กใดๆ ที่จำเป็นสำหรับการเรียกรูทีนย่อย ซึ่งอาจมีความสำคัญเมื่อใช้การเรียกซ้ำ)
คอมพิวเตอร์อิเล็กทรอนิกส์และคอมพิวเตอร์ที่บ้านในยุคแรกๆ มีความจุหน่วยความจำในการทำงานค่อนข้างน้อย ดังนั้นในปี 1949 EDSAC จึงมีหน่วยความจำการทำงานสูงสุด 1,024 คำ 17 บิต และในปี 1980 Sinclair ZX80 เปิดตัวด้วยหน่วยความจำการทำงาน 1,024 ไบต์
คอมพิวเตอร์สมัยใหม่สามารถมีหน่วยความจำได้ค่อนข้างมาก (อาจเป็นกิกะไบต์) ดังนั้นการบีบอัดหน่วยความจำที่ใช้โดยอัลกอริธึมลงในหน่วยความจำที่กำหนดจึงมีความจำเป็นน้อยกว่าเมื่อก่อน อย่างไรก็ตาม การมีอยู่ของหน่วยความจำสามประเภทที่แตกต่างกันมีความสำคัญ:
- แคช (มักเป็น RAM แบบคงที่) - ทำงานด้วยความเร็วที่เทียบได้กับ CPU
- หน่วยความจำกายภาพหลัก (มักจะเป็น RAM แบบไดนามิก) - ทำงานช้ากว่า CPU เล็กน้อย
- หน่วยความจำเสมือน (มักอยู่บนดิสก์) - ให้ภาพลวงตาของหน่วยความจำขนาดใหญ่ แต่ทำงานช้ากว่า RAM หลายพันเท่า
อัลกอริธึมที่หน่วยความจำที่ต้องการพอดีกับแคชของคอมพิวเตอร์จะเร็วกว่าอัลกอริธึมที่พอดีกับหน่วยความจำหลักมาก ซึ่งจะเร็วกว่าอัลกอริธึมที่ใช้พื้นที่เสมือนมาก เรื่องที่ซับซ้อนคือความจริงที่ว่าบางระบบมีแคชถึงสามระดับ ระบบที่ต่างกันมีจำนวนหน่วยความจำประเภทนี้แตกต่างกัน ดังนั้นผลกระทบของหน่วยความจำในอัลกอริทึมจึงอาจแตกต่างกันอย่างมากจากระบบหนึ่งไปอีกระบบหนึ่ง
ในยุคแรกๆ ของการประมวลผลแบบอิเล็กทรอนิกส์ หากอัลกอริทึมและข้อมูลไม่พอดีกับหน่วยความจำหลัก ก็จะใช้งานไม่ได้ ทุกวันนี้ การใช้หน่วยความจำเสมือนทำให้เกิดหน่วยความจำขนาดใหญ่ แต่ก็ต้องแลกกับประสิทธิภาพการทำงาน หากอัลกอริธึมและข้อมูลพอดีกับแคช ก็จะสามารถบรรลุความเร็วที่สูงมากได้ ดังนั้นการลดขนาดหน่วยความจำที่จำเป็นจะช่วยลดเวลาได้ อัลกอริธึมที่ไม่พอดีกับแคชทั้งหมด แต่มีให้ ตำแหน่งของลิงก์สามารถทำงานได้ค่อนข้างรวดเร็ว
ตัวอย่างของอัลกอริธึมที่มีประสิทธิภาพ
คำติชมของสถานะปัจจุบันของการเขียนโปรแกรม
อาจกล่าวว่า:โปรแกรมต่างๆ เริ่มช้าลงอย่างรวดเร็วมากกว่าคอมพิวเตอร์ที่เร็วขึ้น
ในระบบที่แพร่หลาย การดำเนินการคำสั่งแบบแบ่งครึ่งสามารถยืดอายุการใช้งานแบตเตอรี่ได้เป็นสองเท่า และข้อมูลขนาดใหญ่ให้โอกาสสำหรับอัลกอริธึมที่ดีกว่า: การลดจำนวนการดำเนินการจาก N x N เป็น N x log(N) มีผลกระทบอย่างมากต่อ N ขนาดใหญ่... สำหรับ N = 3 หมื่นล้าน การเปลี่ยนแปลงเหล่านี้คล้ายคลึงกับการปรับปรุงทางเทคโนโลยีตลอด 50 ปี
การแข่งขันเพื่ออัลกอริธึมที่ดีที่สุด
การแข่งขันต่อไปนี้ขอเชิญชวนให้มีส่วนร่วมในการพัฒนาอัลกอริธึมที่ดีที่สุด ซึ่งเกณฑ์ด้านคุณภาพจะถูกกำหนดโดยผู้ตัดสิน:
ดูสิ่งนี้ด้วย
- การเข้ารหัสทางคณิตศาสตร์เป็นประเภทของการเข้ารหัสเอนโทรปี ด้วยความยาวโค้ดที่แปรผันได้เพื่อการบีบอัดข้อมูลอย่างมีประสิทธิภาพ
- อาเรย์แบบเชื่อมโยงคือโครงสร้างข้อมูลที่สามารถทำให้มีประสิทธิภาพมากขึ้นเมื่อใช้งาน ต้นไม้ แพทริเซียหรือ จูดี้ อาร์เรย์
- การทดสอบประสิทธิภาพ - วิธีการวัดเวลาดำเนินการเปรียบเทียบในบางกรณี
- กรณีที่ดีที่สุด แย่ที่สุด และปานกลาง- แบบแผนสำหรับการประมาณเวลาดำเนินการสำหรับสามสถานการณ์
- การค้นหาแบบไบนารีเป็นเทคนิคที่ง่ายและมีประสิทธิภาพในการค้นหารายการที่เรียงลำดับ
- ตารางสาขา
เป้าหมายและวัตถุประสงค์ของการบรรยาย: บทนำวิธีการวิเคราะห์ความซับซ้อนและประสิทธิภาพของอัลกอริทึมและโครงสร้างข้อมูล
ประเด็นหลัก: การวิเคราะห์เชิงทดลองและการวิเคราะห์ประสิทธิภาพของอัลกอริทึม
คำกล่าวสุดคลาสสิคของ N. Wirth “โปรแกรมที่ดีคือความสามัคคีของอัลกอริธึมที่คิดมาอย่างดีและโครงสร้างข้อมูลที่มีประสิทธิภาพ”
การวิเคราะห์อัลกอริทึม
แนวคิดของ "อัลกอริทึมและโครงสร้างข้อมูล" เป็นศูนย์กลางของเทคโนโลยีคอมพิวเตอร์ แต่เพื่อที่จะเรียกโครงสร้างข้อมูลและอัลกอริทึมบางอย่างว่า "มีคุณภาพสูงและมีประสิทธิภาพ" ต้องใช้เทคนิคที่แม่นยำในการวิเคราะห์ เนื่องจากเป็นเกณฑ์คุณภาพตามธรรมชาติ จึงเป็นเรื่องธรรมดาที่จะเน้นย้ำเวลาดำเนินการเป็นอันดับแรก สิ่งสำคัญก็คือจำนวนหน่วยความจำและพื้นที่ดิสก์ที่ใช้ไป ความเร็วของการเข้าถึงข้อมูล (ประสิทธิภาพของโครงสร้างข้อมูล) ควรให้ความสนใจกับความน่าเชื่อถือและความน่าเชื่อถือของการตัดสินใจและความมั่นคง
อัลกอริทึมไม่ควรเชื่อมโยงกับการใช้งานเฉพาะ เนื่องจากเครื่องมือการเขียนโปรแกรมที่หลากหลาย อัลกอริธึมในการใช้งานที่แตกต่างกันจึงสามารถสร้างผลลัพธ์ที่มีประสิทธิภาพแตกต่างกันได้
เวลาดำเนินการของอัลกอริทึมหรือการดำเนินการกับโครงสร้างข้อมูลตามกฎแล้วขึ้นอยู่กับปัจจัยหลายประการ วิธีที่ง่ายที่สุดในการกำหนดเวลาที่ต้องใช้ในการดำเนินการอัลกอริทึมคือการวัดเวลาก่อนและหลังการทำงานของอัลกอริทึม
อย่างไรก็ตาม ควรจำไว้ว่าวิธีการประมาณเวลานี้ไม่ถูกต้อง ก่อนอื่น ควรเข้าใจว่าในระบบปฏิบัติการสมัยใหม่สามารถดำเนินการหลายอย่างพร้อมกันได้ และการดำเนินการกรณีทดสอบสามารถรวมกับประเภทอื่น ๆ ได้ ของกิจกรรม นอกจากนี้ควรเข้าใจว่าการพึ่งพาที่มั่นคงสามารถทำได้โดยทำการทดสอบซ้ำ ๆ เท่านั้น มิฉะนั้นเนื่องจากอิทธิพลต่อผลลัพธ์สุดท้ายของการทำงานของปัจจัยสุ่มขึ้นอยู่กับข้อมูลเฉพาะของข้อมูลเริ่มต้นและปัจจัยอื่น ๆ การดำเนินการ เวลาของอัลกอริทึมก็จะเป็นตัวแปรสุ่มเช่นกัน เมื่อทำการวิจัย จำเป็นต้องรันอัลกอริธึมด้วยชุดข้อมูลเริ่มต้นที่แตกต่างกัน โดยปกติแล้วข้อมูลจะถูกสร้างขึ้นแบบสุ่ม ดังนั้นเนื่องจากชุดข้อมูลที่แตกต่างกัน เวลาที่ใช้จึงจะแตกต่างกันด้วย
เมื่อได้ชุดของการประมาณการแล้ว สามารถสร้างกราฟและประมาณได้
การวิเคราะห์ดังกล่าวควรใช้เสมอเมื่อใช้อัลกอริธึมที่ไม่ซับซ้อน ซึ่งคล้ายกับคำแนะนำในการพัฒนาแอปพลิเคชัน โดยใช้สำหรับการดีบัก ไม่ใช่ชุดทดลองของบันทึกหรือองค์ประกอบหลายสิบรายการ แต่เป็นข้อมูลจริงทั้งหมด ซึ่งหลีกเลี่ยงการแก้ไขหรือแม้แต่ การทำงานซ้ำของข้อมูลอัลกอริธึมหรือโครงสร้างหากต่อมาพิสูจน์ได้ว่าไม่สามารถทำได้ ด้วยชุดผลการทดลอง คุณสามารถทำการประมาณค่าและคาดการณ์ และกำหนดพฤติกรรมของอัลกอริทึมในสภาวะจริงได้
โดยทั่วไป เราสามารถพูดได้ว่าเวลาดำเนินการของอัลกอริทึมหรือวิธีโครงสร้างข้อมูลจะเพิ่มขึ้นเมื่อขนาดของแหล่งข้อมูลเพิ่มขึ้น แม้ว่าจะขึ้นอยู่กับประเภทของข้อมูลด้วย แม้ว่าขนาดจะเท่ากันก็ตาม นอกจากนี้ เวลาในการดำเนินการยังขึ้นอยู่กับฮาร์ดแวร์ (โปรเซสเซอร์ ความถี่สัญญาณนาฬิกา ขนาดหน่วยความจำ พื้นที่ดิสก์ ฯลฯ) และซอฟต์แวร์ (สภาพแวดล้อมการทำงาน ภาษาการเขียนโปรแกรม คอมไพเลอร์ ล่าม ฯลฯ) ที่ใช้ดำเนินการ คอมไพล์ และ การดำเนินการของอัลกอริทึม ตัวอย่างเช่น สิ่งอื่น ๆ ทั้งหมดเท่ากัน เวลาดำเนินการของอัลกอริทึมสำหรับแหล่งข้อมูลจำนวนหนึ่งจะน้อยลงเมื่อใช้คอมพิวเตอร์ที่ทรงพลังกว่าหรือเมื่อเขียนอัลกอริทึมเป็นโปรแกรมในรหัสเครื่องเมื่อเปรียบเทียบกับการประมวลผลด้วยเครื่องเสมือน ตีความมันเป็น bytecodes
ข้อสรุปก็คือว่าการดำเนินการวิเคราะห์เชิงประจักษ์ของอัลกอริทึมนั้นไม่น่าเชื่อถืออย่างแท้จริง ข้อเสียเปรียบหลักสามารถลดลงได้สามจุดต่อไปนี้:
1) การทดลองสามารถทำได้โดยใช้ชุดข้อมูลเริ่มต้นที่จำกัดเท่านั้น ผลลัพธ์ที่ได้รับโดยใช้ชุดอื่นจะไม่ถูกนำมาพิจารณา
2) เพื่อเปรียบเทียบประสิทธิภาพของอัลกอริธึมทั้งสองจำเป็นต้องทำการทดลองเพื่อกำหนดเวลาดำเนินการบนฮาร์ดแวร์และซอฟต์แวร์เดียวกัน
3) เพื่อศึกษาเวลาดำเนินการของอัลกอริธึมเชิงทดลองจำเป็นต้องดำเนินการนำไปใช้และดำเนินการ
ดังนั้นเราจึงจำเป็นต้องใช้วิธีการวิเคราะห์ทั่วไปในการวิเคราะห์อัลกอริธึมซึ่งช่วยให้:
1) คำนึงถึงข้อมูลอินพุตประเภทต่างๆ
2) ช่วยให้คุณสามารถประเมินประสิทธิภาพสัมพัทธ์ของอัลกอริธึมทั้งสอง โดยไม่คำนึงถึงฮาร์ดแวร์และซอฟต์แวร์
3) สามารถดำเนินการได้ตามคำอธิบายของอัลกอริทึมโดยไม่ต้องมีการใช้งานหรือการทดลองโดยตรง
สาระสำคัญของการวิเคราะห์ทั่วไปคือฟังก์ชัน f=f(n1, .., nm) ถูกกำหนดให้กับอัลกอริธึมบางตัว ในรูปแบบที่ง่ายที่สุด มันเป็นฟังก์ชันของตัวแปรตัวหนึ่ง n1 ซึ่งเป็นจำนวนข้อมูลอินพุต อย่างไรก็ตาม อาจมีตัวแปรอื่นๆ เช่น ความแม่นยำในการคำนวณหรือความน่าเชื่อถือ ดังนั้นเพื่อตรวจสอบว่าจำนวนเฉพาะเป็นจำนวนเฉพาะในกรณีของตัวเลขจำนวนมากหรือไม่ (ความยาวของการแทนไบนารี่มากกว่า 200 บิต) จึงใช้วิธีการความน่าจะเป็นซึ่งความน่าเชื่อถือสามารถเปลี่ยนแปลงได้ ฟังก์ชันที่รู้จักกันดีที่สุดคือฟังก์ชันเชิงเส้น กำลัง และลอการิทึม ดังนั้นคุณควรใช้เวลาในการจดจำพื้นฐานของการทำงานกับพวกเขา
เมื่อสร้างอัลกอริทึม ขั้นตอนแรกจะเกิดขึ้นโดยใช้ไม่ใช่ภาษาโปรแกรม แต่เป็นคำอธิบายในภาษามนุษย์ คำอธิบายดังกล่าวไม่ใช่โปรแกรม แต่ในขณะเดียวกันก็มีโครงสร้างมากกว่าข้อความธรรมดา โดยเฉพาะอย่างยิ่งคำอธิบาย "ระดับสูง" ผสมผสานภาษาธรรมชาติและโครงสร้างภาษาโปรแกรมทั่วไปเข้าด้วยกัน ทำให้สามารถเข้าถึงได้แต่ยังให้ข้อมูลได้ คำอธิบายดังกล่าวช่วยอำนวยความสะดวกในการวิเคราะห์โครงสร้างข้อมูลหรืออัลกอริธึมในระดับสูง คำอธิบายดังกล่าวมักเรียกว่ารหัสเทียม ควรสังเกตว่า pseudocode มักจะมีประโยชน์ในการวิเคราะห์มากกว่าโค้ดในภาษาการเขียนโปรแกรมเฉพาะ
บางครั้งมีความจำเป็นต้องพิสูจน์ข้อความบางอย่างที่เกี่ยวข้องกับโครงสร้างข้อมูลหรืออัลกอริธึมบางอย่าง ตัวอย่างเช่น คุณต้องแสดงให้เห็นถึงความถูกต้องและความเร็วในการดำเนินการของอัลกอริทึม ในการพิสูจน์ข้อความอย่างเคร่งครัด จำเป็นต้องใช้ภาษาทางคณิตศาสตร์ซึ่งจะทำหน้าที่เป็นข้อพิสูจน์หรือเหตุผลของข้อความ มีวิธีง่ายๆ หลายวิธีในการพิสูจน์สิ่งนี้
บางครั้งข้อความจะถูกเขียนในรูปแบบทั่วไป: “เซต s ประกอบด้วยองค์ประกอบ x พร้อมคุณสมบัติ v เพื่อพิสูจน์ข้อความนี้ ก็เพียงพอที่จะยกตัวอย่าง x “เป็นของ” s ซึ่งมีคุณสมบัตินี้ ตามกฎแล้ว ในรูปแบบทั่วไป ข้อความที่ไม่น่าเป็นไปได้จะถูกเขียนขึ้น เช่น “ทุกองค์ประกอบ x ของเซต s มีคุณสมบัติ P” เพื่อพิสูจน์ความผิดพลาดของข้อความนี้ ก็เพียงพอที่จะยกตัวอย่าง: x “เป็นของ” s ซึ่งไม่มีคุณสมบัติ P ในกรณีนี้ องค์ประกอบ x จะทำหน้าที่เป็นตัวอย่างที่โต้แย้ง
ตัวอย่าง:ระบุว่าจำนวนใดๆ ในรูปแบบ 2^n - 1 จะเป็นจำนวนเฉพาะ ถ้า n เป็นจำนวนเต็มที่มากกว่า 1 ข้อความสั่งนี้เป็นเท็จ
การพิสูจน์:เพื่อพิสูจน์ว่ามีคนผิด คุณต้องหาตัวอย่างที่โต้แย้ง
นี่คือตัวอย่างแย้ง: 2^4 - 1 = 15, 15= 3 * 5
มีอีกวิธีหนึ่ง ขึ้นอยู่กับการพิสูจน์โดยขัดแย้ง (ใช้การปฏิเสธ) วิธีการหลักในกรณีนี้คือการขัดแย้งและขัดแย้งกัน การใช้วิธีตัดกันนั้นคล้ายกับการมิเรอร์: เพื่อพิสูจน์ว่า “ถ้า x เป็นจริง แล้ว y จะเป็นจริง” เราจะยืนยันในทางตรงกันข้าม “ถ้า y เป็นเท็จ แล้ว x ก็เป็นเท็จ” จากมุมมองเชิงตรรกะ ข้อความเหล่านี้เหมือนกัน แต่นิพจน์ที่สองซึ่งเป็น cotroposition ของนิพจน์แรกจะสะดวกกว่า
ตัวอย่าง:ถ้า a*b เป็นเลขคี่ แล้ว a เป็นเลขคี่ หรือ b เป็นเลขคี่
การพิสูจน์:เพื่อพิสูจน์ข้อความนี้ ให้พิจารณาสิ่งที่ตรงกันข้าม: “ถ้า a เป็นจำนวนคู่และ b เป็นเลขคี่ แล้ว a*b ก็เป็นเลขคู่ กำหนดให้ a = 2*x สำหรับจำนวนเต็ม x บางค่า จากนั้น a*b = 2*i*b ดังนั้นผลคูณ a*b จึงเป็นเลขคู่
เมื่อใช้วิธีการพิสูจน์โดยขัดแย้ง การใช้ตรรกะจะเป็นประโยชน์
A หรือ b = กำหนดให้ a หรือ b ถูกดำเนินการ หรือทั้ง a และ b ในเวลาเดียวกัน
- a และ b = กำหนดให้ a และ b ดำเนินการพร้อมกัน
- a xor b = ต้องมีการดำเนินการของ a แต่ไม่ใช่ b หรือ b แต่ไม่ใช่ a
เมื่อใช้วิธีการขัดแย้งเพื่อพิสูจน์ว่าข้อความ q เป็นจริง ก่อนอื่นให้ถือว่า q เป็นเท็จ แล้วจึงแสดงว่าสมมติฐานนั้นนำไปสู่ความขัดแย้ง (เช่น 2 * 2<>4) เมื่อเกิดข้อขัดแย้งดังกล่าว เราสามารถโต้แย้งได้ว่าสถานการณ์ที่ q เป็นเท็จนั้นไม่มีอยู่จริง ดังนั้น q จึงเป็นจริง
ในกรณีส่วนใหญ่ ข้อความเกี่ยวกับเวลาการทำงานของโปรแกรมหรือการใช้พื้นที่จะใช้พารามิเตอร์จำนวนเต็ม n (แสดงถึง "ขนาด" ของปัญหา) จากนั้นเมื่อเรากำหนดคำสั่ง x(n) ดังนั้นสำหรับชุดของค่า n คำสั่งดังกล่าวจะเทียบเท่ากัน เนื่องจากข้อความนี้ใช้กับชุดตัวเลขที่ "ไม่มีที่สิ้นสุด" จึงเป็นไปไม่ได้ที่จะให้การพิสูจน์โดยตรงที่ละเอียดถี่ถ้วน ในสถานการณ์เช่นนี้ จะใช้วิธีการเหนี่ยวนำ วิธีการอุปนัยนั้นขึ้นอยู่กับข้อเท็จจริง สำหรับ n > 1 ใดๆ มีลำดับการกระทำที่มีจำกัดซึ่งเริ่มต้นด้วยสิ่งที่รู้ว่าเป็นจริงและนำไปสู่การพิสูจน์ว่า q(n) เป็นจริงในที่สุด ดังนั้น การพิสูจน์โดยการเหนี่ยวนำจะเริ่มต้นด้วยข้อความที่ว่า q(n) เป็นจริงสำหรับ n=1,2,3 เป็นต้น จนถึงค่าคงที่ k ต่อไปเราจะพิสูจน์ว่า “ขั้นตอน” ถัดไปของการเหนี่ยวนำ q(n+1), q(n+2) ก็เป็นจริงสำหรับ n > k เช่นกัน
เมื่อวิเคราะห์อัลกอริธึมการคำนวณจำนวนการดำเนินการและเวลาดำเนินการไม่ควรคำนึงถึง "รายละเอียดเล็ก ๆ น้อย ๆ " ในทางปฏิบัติ จะใช้แนวคิดเรื่องฟังก์ชันขนาดใหญ่ เกี่ยวกับ- สมมติว่ามีฟังก์ชัน f(n) และ g(n) อยู่ 2 ฟังก์ชัน ให้ถือว่า f(n)<= O(g(n)) , т.е. функция О ограничивает сверху значения функции f, начиная с n=n0.
ตัวอย่างเช่น อัลกอริทึมสำหรับการนับจำนวนองค์ประกอบเท่ากับศูนย์ในอาร์เรย์อธิบายโดย O(n) โดยที่ n คือจำนวนองค์ประกอบ
1) 20n3+7.2n2-21.78n + 5 อธิบายเป็น O(n3)
2)xn-2 + a(0) เรียกว่า O(xn)
2) 3*log(n) + log(log(n)) อธิบายว่าเป็น O(log(n))
3) 2100 อธิบายว่าเป็น O(1)
4) 5/n เรียกว่า O(1/n)
โปรดทราบว่าฟังก์ชัน o(n) จะจำกัดฟังก์ชันต้นทุนเวลาเป้าหมายจากด้านบน แต่คุณควรพยายามเลือกฟังก์ชัน O(n) ที่มีความแม่นยำสูงสุดเสมอ
ฟังก์ชัน O ที่มีชื่อเสียงที่สุดโดยเรียงจากน้อยไปหามาก:
เมื่อใช้การวิเคราะห์เชิงเส้นกำกับ ระวังว่าเมื่อคุณใช้สัญลักษณ์ O คุณมักจะละเลยค่าคงที่และการบวกค่าคงที่ อย่างไรก็ตาม หากค่านี้มากเพียงพอ แม้ว่ารูปแบบของฟังก์ชัน O(1) จะดีกว่าอัลกอริทึมที่อธิบายโดยฟังก์ชัน O(n) แน่นอนว่า มันคืออัลกอริธึมตัวที่สองที่จะได้นำไปใช้จริง
ขึ้นอยู่กับประเภทของฟังก์ชัน f(n) คลาสของความซับซ้อนของอัลกอริทึมต่อไปนี้จะแตกต่างกัน
คลาสความซับซ้อนของอัลกอริทึมขึ้นอยู่กับฟังก์ชันความซับซ้อน | |
ดูฉ(n) | ลักษณะของคลาสอัลกอริธึม |
คำสั่งส่วนใหญ่สำหรับฟังก์ชันส่วนใหญ่จะถูกดำเนินการอย่างน้อยหนึ่งครั้ง หากคำสั่งทั้งหมดในโปรแกรมมีคุณสมบัตินี้ เวลาดำเนินการของโปรแกรมจะคงที่ | |
ล็อกเอ็น | เมื่อเวลาดำเนินการของโปรแกรมเป็นลอการิทึม โปรแกรมจะช้าลงเมื่อ N เพิ่มขึ้น โดยทั่วไปเวลาดำเนินการดังกล่าวจะเชื่อมโยงกับโปรแกรมที่ลดปัญหาใหญ่ให้เหลือชุดของปัญหาย่อยที่มีขนาดเล็กลง โดยลดขนาดของปัญหาตามปัจจัยคงที่บางอย่างในแต่ละขั้นตอน การเปลี่ยนฐานไม่ส่งผลกระทบอย่างมากต่อการเปลี่ยนแปลงค่าลอการิทึม: n |
เอ็น | เมื่อเวลาดำเนินการของโปรแกรมเป็นแบบเส้นตรง โดยปกติจะหมายความว่าแต่ละองค์ประกอบอินพุตได้รับการประมวลผลเพียงเล็กน้อย |
เอ็น ล็อก เอ็น | รันไทม์เป็นสัดส่วนกับ N log N เกิดขึ้นเมื่ออัลกอริธึมแก้ไขปัญหาโดยการแบ่งมันออกเป็นปัญหาย่อยที่มีขนาดเล็กลง แก้ไขอย่างอิสระ จากนั้นจึงรวมโซลูชันเข้าด้วยกัน |
ยังไม่มีข้อความ 2 | เมื่อเวลาทำงานของอัลกอริทึมเป็นแบบกำลังสอง จะเป็นประโยชน์สำหรับการใช้งานจริงในการแก้ปัญหาที่มีขนาดค่อนข้างเล็ก โดยทั่วไปเวลาดำเนินการกำลังสองจะปรากฏในอัลกอริธึมที่ประมวลผลคู่ของรายการข้อมูลทั้งหมด (อาจอยู่ในลูปที่ซ้อนกันสองชั้น) |
ยังไม่มีข้อความ 3 | อัลกอริธึมที่คล้ายกันซึ่งประมวลผลองค์ประกอบข้อมูลสามชุด (อาจอยู่ในลูปซ้อนสาม) มีเวลาดำเนินการแบบลูกบาศก์และใช้งานได้จริงสำหรับปัญหาเล็กๆ เท่านั้น |
2N | มีอัลกอริธึมเพียงไม่กี่ตัวที่มีเวลารันแบบเอ็กซ์โปเนนเชียลเท่านั้นที่มีการใช้งานจริง แม้ว่าอัลกอริธึมดังกล่าวจะเกิดขึ้นตามธรรมชาติเมื่อพยายามแก้ไขปัญหาโดยตรง เช่น การใช้กำลังดุร้าย |
ตามวิธีการทางคณิตศาสตร์สำหรับการศึกษาฟังก์ชันซีมโทติกของความซับซ้อนที่อนันต์ มีการระบุอัลกอริธึมห้าคลาส
1. คลาสของอัลกอริธึมที่รวดเร็วซึ่งมีเวลาดำเนินการคงที่ ฟังก์ชันความซับซ้อนคือ O(1) สถานะระดับกลางถูกครอบครองโดยอัลกอริธึมที่มีความซับซ้อน O(log N) ซึ่งจัดอยู่ในคลาสนี้ด้วย
2. คลาสของอัลกอริธึมเชิงตรรกยะหรือพหุนาม ฟังก์ชันความซับซ้อนถูกกำหนดแบบพหุนามจากพารามิเตอร์อินพุต ตัวอย่างเช่น O(N), O(N 2, O(N 3)
3. คลาสของอัลกอริธึมย่อยเอ็กซ์โปเนนเชียลที่มีระดับความซับซ้อน O(N log N)
4.คลาสของอัลกอริธึมเอ็กซ์โปเนนเชียลที่มีระดับความซับซ้อน O(2 N)
5.คลาสของอัลกอริธึมโอเวอร์เอ็กซ์โปเนนเชียล มีอัลกอริธึมที่มีความซับซ้อนแฟกทอเรียล แต่โดยทั่วไปแล้วไม่มีการประยุกต์ใช้ในทางปฏิบัติ
สถานะหน่วยความจำระหว่างการดำเนินการอัลกอริทึมถูกกำหนดโดยค่าที่จำเป็นต้องจัดสรรพื้นที่บางส่วน ในกรณีนี้ ในระหว่างการแก้ปัญหา สามารถใช้เซลล์เพิ่มเติมได้อีกจำนวนหนึ่ง ตามจำนวนหน่วยความจำที่ต้องการโดยอัลกอริทึม A สำหรับอินพุต D เราหมายถึงจำนวนเซลล์หน่วยความจำสูงสุดที่ใช้ระหว่างการดำเนินการของอัลกอริทึม ความซับซ้อนของความจุของอัลกอริทึมถูกกำหนดให้เป็นค่าประมาณเชิงเส้นกำกับของฟังก์ชันความจุหน่วยความจำกรณีที่แย่ที่สุดของอัลกอริทึม
ดังนั้น ความซับซ้อนของทรัพยากรของอัลกอริธึมในกรณีที่แย่ที่สุด โดยเฉลี่ย และดีที่สุดถูกกำหนดให้เป็นคู่ลำดับของคลาสของฟังก์ชันของความซับซ้อนของเวลาและความจุ ซึ่งระบุโดยสัญลักษณ์เชิงเส้นกำกับและสอดคล้องกับกรณีที่อยู่ระหว่างการพิจารณา
โครงสร้างอัลกอริธึมหลักในการเขียนโปรแกรมตามขั้นตอนมีดังต่อไปนี้ การแตกแขนง และการวนซ้ำ เพื่อให้ได้ฟังก์ชันที่ซับซ้อนสำหรับกรณีที่ดีที่สุด ค่าเฉลี่ย และแย่ที่สุดด้วยมิติอินพุตคงที่ จำเป็นต้องคำนึงถึงความแตกต่างในการประเมินโครงสร้างอัลกอริทึมหลักด้วย
- ความซับซ้อนของโครงสร้าง "ต่อไปนี้" คือผลรวมของความซับซ้อนของบล็อกที่ต่อกัน: f=f 1 +f 2 +...+f n
- ความซับซ้อนของการออกแบบ "การแยกสาขา" ถูกกำหนดโดยความน่าจะเป็นของการเปลี่ยนไปใช้คำสั่งแต่ละคำสั่งซึ่งกำหนดโดยเงื่อนไข ในขณะเดียวกัน การตรวจสอบเงื่อนไขก็มีความซับซ้อนเช่นกัน ในการคำนวณความซับซ้อนของกรณีที่เลวร้ายที่สุด สามารถเลือกบล็อกการแยกย่อยที่มีความซับซ้อนมากที่สุดได้ สำหรับกรณีที่ดีที่สุด สามารถเลือกบล็อกที่มีความซับซ้อนน้อยกว่าได้ f ถ้า =f 1 +f แล้ว p แล้วก็ +f อย่างอื่น (1-p แล้ว)
- ความซับซ้อนของโครงสร้าง "ลูป" ถูกกำหนดโดยการคำนวณเงื่อนไขการสิ้นสุดของลูป (โดยปกติจะเป็นลำดับ 0(1)) และผลิตภัณฑ์ของจำนวนการวนซ้ำที่เสร็จสมบูรณ์ของลูปด้วยจำนวนการดำเนินการที่ใหญ่ที่สุดที่เป็นไปได้ของตัวลูป หากใช้ลูปแบบซ้อน ความซับซ้อนของมันจะถูกคูณ
ดังนั้น เพื่อประเมินความซับซ้อนของอัลกอริธึม สามารถกำหนดวิธีการทั่วไปในการรับฟังก์ชันความซับซ้อนได้
- การสลายตัวของอัลกอริทึมเกี่ยวข้องกับการระบุโครงสร้างพื้นฐานในอัลกอริทึมและการประมาณความซับซ้อน ในกรณีนี้ จะพิจารณาโครงสร้างอัลกอริทึมหลักต่อไปนี้
- การวิเคราะห์ความเข้มข้นของแรงงานทีละบรรทัดสำหรับการปฏิบัติงานด้วยภาษาพื้นฐานหมายถึงการวิเคราะห์แบบสะสม (โดยคำนึงถึงการปฏิบัติงานทั้งหมด) หรือการวิเคราะห์การปฏิบัติงาน (โดยคำนึงถึงความซับซ้อนของการปฏิบัติงานแต่ละครั้ง)
- องค์ประกอบผกผันของฟังก์ชันความซับซ้อนตามวิธีการวิเคราะห์โครงสร้างอัลกอริทึมพื้นฐานสำหรับกรณีที่ดีที่สุด ค่าเฉลี่ย และแย่ที่สุด
คุณลักษณะในการประเมินประสิทธิภาพทรัพยากรของอัลกอริธึมแบบเรียกซ้ำคือความจำเป็นในการคำนึงถึงต้นทุนหน่วยความจำเพิ่มเติมและกลไกในการจัดระเบียบการเรียกซ้ำ ดังนั้นความซับซ้อนของการใช้งานอัลกอริธึมแบบเรียกซ้ำจึงสัมพันธ์กับจำนวนการดำเนินการที่ทำในระหว่างการเรียกซ้ำหนึ่งครั้ง รวมถึงจำนวนการโทรดังกล่าว ค่าใช้จ่ายในการส่งคืนค่าและการถ่ายโอนการควบคุมไปยังจุดโทรก็ถูกนำมาพิจารณาด้วย เมื่อประมาณค่าหน่วยความจำสแต็กที่ต้องการ คุณต้องคำนึงว่า ณ จุดใดจุดหนึ่ง ไม่ใช่ส่วนการเรียกซ้ำที่ถูกจัดเก็บไว้ในสแต็ก แต่เป็นลูกโซ่ของการเรียกซ้ำ ดังนั้นขนาดสแต็กจึงถูกกำหนดโดยจำนวนการโทรซ้ำที่เกิดขึ้นพร้อมกันสูงสุดที่เป็นไปได้
ห้องสมุดของโปรแกรมเมอร์
“หากการดีบักเป็นกระบวนการกำจัดข้อผิดพลาด การเขียนโปรแกรมควรเป็นกระบวนการแนะนำข้อผิดพลาดเหล่านั้น”
อี. ไดจค์สตรา
1.2. ทำไมต้องศึกษาอัลกอริทึม? ประสิทธิภาพของอัลกอริทึม
ประการแรก อัลกอริธึมเป็นองค์ประกอบสำคัญในการแก้ปัญหาในด้านต่างๆ ของวิทยาการคอมพิวเตอร์ อัลกอริทึมมีบทบาทสำคัญในขั้นตอนการพัฒนาเทคโนโลยีในปัจจุบัน ที่นี่คุณสามารถจำงานทั่วไปเช่น:
- การแก้สมการทางคณิตศาสตร์ที่ซับซ้อนต่างกัน การหาผลคูณของเมทริกซ์ เมทริกซ์ผกผัน
- ค้นหาวิธีที่เหมาะสมที่สุดในการขนส่งสินค้าและผู้คน
- ค้นหาตัวเลือกที่เหมาะสมที่สุดสำหรับการกระจายทรัพยากรระหว่างโหนดต่างๆ (ผู้ผลิต เครื่องจักร คนงาน ผู้ประมวลผล ฯลฯ)
- ค้นหาลำดับในจีโนมที่ตรงกัน
- ค้นหาข้อมูลบนอินเทอร์เน็ตทั่วโลก
- การตัดสินใจทางการเงินในอีคอมเมิร์ซ
- การประมวลผลและการวิเคราะห์ข้อมูลเสียงและวิดีโอ
รายการนี้ดำเนินต่อไปเรื่อยๆ และในความเป็นจริงแทบจะเป็นไปไม่ได้เลยที่จะค้นหาสาขาวิชาวิทยาการคอมพิวเตอร์และสารสนเทศศาสตร์ที่ไม่ได้ใช้อัลกอริธึมบางอย่าง
ประการที่สอง อัลกอริธึมคุณภาพสูงและมีประสิทธิภาพสามารถเป็นตัวเร่งให้เกิดความก้าวหน้าในอุตสาหกรรมที่ห่างไกลจากวิทยาการคอมพิวเตอร์ (กลศาสตร์ควอนตัม เศรษฐศาสตร์และการเงิน ทฤษฎีวิวัฒนาการ) เมื่อมองแวบแรก
และประการที่สาม การศึกษาอัลกอริธึมยังเป็นกระบวนการที่น่าสนใจอย่างไม่น่าเชื่อ ซึ่งพัฒนาความสามารถทางคณิตศาสตร์และการคิดเชิงตรรกะของเรา
1.3. ประสิทธิภาพของอัลกอริทึม
สมมติว่าความเร็วของคอมพิวเตอร์และจำนวนหน่วยความจำสามารถเพิ่มขึ้นได้อย่างไม่มีกำหนด แล้วจะต้องศึกษา Algorithm ไหม? ใช่ แต่เพียงเพื่อแสดงให้เห็นว่าวิธีแยกการเชื่อมต่อมีเวลาจำกัดและให้คำตอบที่ถูกต้องเท่านั้น หากคอมพิวเตอร์ทำงานเร็วอย่างไม่สิ้นสุด วิธีแก้ปัญหาที่ถูกต้องตามอำเภอใจก็สามารถทำได้ แน่นอนว่ามักจะเลือกวิธีที่ง่ายต่อการนำไปใช้
ทุกวันนี้ คอมพิวเตอร์มีพลังมาก แต่ความเร็วไม่ได้ไม่มีที่สิ้นสุด และหน่วยความจำก็เช่นกัน ดังนั้นในแคลคูลัส ทรัพยากรจึงมีจำกัดพอๆ กับจำนวนหน่วยความจำที่ต้องการ ทรัพยากรเหล่านี้ควรใช้อย่างชาญฉลาดซึ่งได้รับการอำนวยความสะดวกโดยการใช้อัลกอริทึมที่มีประสิทธิภาพในแง่ของการใช้เวลาและทรัพยากรหน่วยความจำ
อัลกอริธึมที่ออกแบบมาเพื่อแก้ไขปัญหาเดียวกันมักจะมีประสิทธิภาพแตกต่างกันอย่างมาก ความแตกต่างเหล่านี้สามารถสังเกตได้ชัดเจนกว่าที่เกิดจากฮาร์ดแวร์และซอฟต์แวร์ที่แตกต่างกันมาก
ตามที่ระบุไว้ข้างต้น ส่วนนี้จะเน้นไปที่งานการเรียงลำดับ อัลกอริธึมแรกที่จะพิจารณา การเรียงลำดับแบบรวม ต้องใช้เวลาในการทำงาน จำนวนโดยประมาณคือ c 1 n 2 โดยที่ n คือขนาดของข้อมูลอินพุต (จำนวนองค์ประกอบในลำดับที่จะเรียงลำดับ) c 1 เป็นค่าคงที่บางค่า นิพจน์นี้ระบุว่าเวลาทำงานของอัลกอริทึมขึ้นอยู่กับปริมาณของแหล่งข้อมูลอย่างไร ในกรณีของการเรียงลำดับแบบรวม การพึ่งพานี้จะเป็นกำลังสอง อัลกอริธึมที่สอง การเรียงลำดับแบบผสาน ต้องใช้เวลา จำนวนประมาณเป็น 2 nLog 2 n โดยทั่วไป ค่าคงที่การเรียงลำดับแบบรวมจะน้อยกว่าค่าคงที่การเรียงลำดับแบบผสาน นั่นคือ c12 เติบโตเร็วกว่าเมื่อ n เพิ่มขึ้นมากกว่าฟังก์ชัน ILog 2 n และสำหรับค่าบางค่า n = n 0 จะถึงช่วงเวลาหนึ่งเมื่ออิทธิพลของความแตกต่างในค่าคงที่สิ้นสุดลงและในอนาคตฟังก์ชัน c 2 nLog 2 n จะน้อยกว่า c 1 n 2 สำหรับ n > n 0 ใด ๆ
เพื่อสาธิตสิ่งนี้ ให้พิจารณาคอมพิวเตอร์สองเครื่อง - A และ B คอมพิวเตอร์ A จะเร็วกว่าและรันอัลกอริธึมการเรียงลำดับ ส่วนคอมพิวเตอร์ B จะช้ากว่าและรันอัลกอริธึมการเรียงลำดับแบบผสาน คอมพิวเตอร์ทั้งสองเครื่องจะต้องเรียงลำดับชุดที่ประกอบด้วยตัวเลขหนึ่งล้านตัว สมมติว่าคอมพิวเตอร์ A ดำเนินการหนึ่งพันล้านรายการต่อวินาที และคอมพิวเตอร์ B เพียงสิบล้านรายการ มี A ทำงานเร็วกว่า B ถึง 100 เท่า เพื่อให้เห็นความแตกต่างได้ชัดเจนยิ่งขึ้น สมมติว่าโค้ดสำหรับวิธีเปิดใช้งานนั้นเขียนโดยผู้ที่เก่งที่สุด โปรแกรมเมอร์ในโลกโดยใช้คำสั่งให้กับโปรเซสเซอร์และในการเรียงลำดับตัวเลข n ด้วยอัลกอริทึมนี้คุณต้องดำเนินการ 2n 2 (นั่นคือ C 1 = 2) การเรียงลำดับแบบผสานบนคอมพิวเตอร์ B เขียนโดยโปรแกรมเมอร์มือใหม่โดยใช้ภาษาระดับสูง และโค้ดผลลัพธ์ต้องใช้การดำเนินการ 50nlog 2 n (นั่นคือ c 2 = 50) ดังนั้น หากต้องการเรียงลำดับตัวเลขนับล้าน คอมพิวเตอร์ A จึงจำเป็นต้องใช้
และไปยังคอมพิวเตอร์ B -
ดังนั้น การใช้โค้ดที่เวลาทำงานเพิ่มขึ้นช้ากว่า แม้ว่าคอมพิวเตอร์จะเสียและคอมไพเลอร์ไม่ดี ก็ยังต้องใช้เวลา CPU น้อยกว่าตามลำดับ! สำหรับการเรียงลำดับตัวเลข 10,000,000 หลัก ข้อดีของการเรียงลำดับแบบผสานจะสังเกตเห็นได้ชัดเจนยิ่งขึ้น: ในขณะที่การเรียงลำดับแบบรวมต้องใช้เวลาประมาณ 2.3 วันสำหรับงานดังกล่าว จากนั้นสำหรับการเรียงลำดับแบบผสานจะใช้เวลาน้อยกว่า 20 นาที กฎทั่วไปคือ ยิ่งจำนวนองค์ประกอบที่จะเรียงลำดับมากเท่าใด ข้อได้เปรียบของการเรียงลำดับแบบผสานก็จะยิ่งมากขึ้นเท่านั้น ตัวอย่างข้างต้นแสดงให้เห็นว่าอัลกอริทึม เช่นเดียวกับซอฟต์แวร์คอมพิวเตอร์ เทคโนโลยี- ประสิทธิภาพโดยรวมของระบบขึ้นอยู่กับประสิทธิภาพของอัลกอริทึมพอๆ กับประสิทธิภาพของฮาร์ดแวร์
ดังนั้นจึงมีการพิจารณาตัวเลือกต่างๆ สำหรับเครื่องคำนวณ ตั้งแต่เครื่องทัวริงที่ง่ายที่สุดไปจนถึงสภาพแวดล้อมการประมวลผลที่เป็นเนื้อเดียวกัน ทั้งหมดนี้สามารถใช้เพื่อแก้ปัญหาที่มีอัลกอริทึมอยู่ได้ จากแบบจำลองเหล่านี้ มีการสร้างแบบจำลองการคำนวณเฉพาะทางมากขึ้น กล่าวคือ โปรแกรมเลขคณิตแบบไม่แยกสาขา การคำนวณระดับบิต การคำนวณเวกเตอร์ไบนารี่ และแผนผังการตัดสินใจ
อัลกอริธึมมีลักษณะดังต่อไปนี้:
ก) ความซับซ้อน;
b) ความเข้มข้นของแรงงาน;
c) ความน่าเชื่อถือ ฯลฯ
มีเกณฑ์มากมายในการประเมินความซับซ้อนของอัลกอริทึม ส่วนใหญ่แล้วเราจะสนใจ ลำดับการเจริญเติบโตเวลาและความจุหน่วยความจำที่จำเป็นในการแก้ปัญหาเมื่อปริมาณข้อมูลอินพุตเพิ่มขึ้น ให้เราเชื่อมโยงกับแต่ละงานเฉพาะจำนวนหนึ่งที่เรียกว่า ขนาด- ตัวอย่างเช่น ขนาดของปัญหาการคูณเมทริกซ์อาจเป็นขนาดที่ใหญ่ที่สุดของเมทริกซ์ตัวประกอบ ขนาดของปัญหาบนกราฟอาจเป็นจำนวนขอบของกราฟที่กำหนด เป็นต้น
เวลาที่อัลกอริธึมใช้เป็นฟังก์ชันของขนาดปัญหาเรียกว่า ความซับซ้อนของเวลาอัลกอริทึมนี้ เรียกว่าพฤติกรรมของความซับซ้อนนี้ในขีดจำกัดเมื่อขนาดของปัญหาเพิ่มขึ้น ความซับซ้อนของเวลาเชิงเส้นกำกับ- กำหนดไว้เช่นเดียวกัน ความซับซ้อนของความจุและ ความซับซ้อนของความจุซีมโทติค.
แรงจูงใจที่สำคัญในการพิจารณาแบบจำลองการคำนวณอย่างเป็นทางการคือความปรารถนาที่จะเปิดเผยความซับซ้อนในการคำนวณของปัญหาต่างๆ เพื่อให้ได้ขอบเขตล่างของเวลาในการคำนวณ เพื่อแสดงให้เห็นว่าไม่มีอัลกอริธึมใดที่สามารถทำงานได้ให้เสร็จสิ้นภายในเวลาไม่ถึงระยะเวลาที่กำหนด จำเป็นต้องมีคำจำกัดความที่แม่นยำและบางครั้งก็มีความเชี่ยวชาญสูงว่าอัลกอริธึมคืออะไร ตัวอย่างหนึ่งของคำจำกัดความดังกล่าวคือเครื่องจักรทัวริง
4.1.1. เครื่องเฟรมและเฟรม*
พิจารณารถยนต์สองคัน:
1. เครื่องที่มีการเข้าถึงหน่วยความจำแบบสุ่ม (เครื่องที่อยู่การเข้าถึงที่เท่าเทียมกัน - RAM) จำลองคอมพิวเตอร์ที่มีแอดเดอร์หนึ่งตัว ซึ่งคำสั่งโปรแกรมไม่สามารถเปลี่ยนแปลงได้เอง
2. โมเดลโปรแกรมที่เก็บไว้คือเครื่องที่มีการเข้าถึงหน่วยความจำโดยสุ่มและมีความสามารถในการแก้ไขคำสั่ง (RAM*)
รูปที่ 2.9 โครงสร้างเครื่อง RAM (RAM*)
สำหรับ RAM โปรแกรมไม่ได้ถูกเขียนลงหน่วยความจำ ดังนั้นโปรแกรมจึงไม่ปรับเปลี่ยนตัวเอง โปรแกรมคือลำดับของคำสั่งที่มีป้ายกำกับ มีคำสั่งทางคณิตศาสตร์, คำสั่ง I/O, คำสั่งการกำหนดที่อยู่ทางอ้อม และคำสั่งสาขา การคำนวณทั้งหมดดำเนินการในรีจิสเตอร์ r 0 (แอดเดอร์) ซึ่งเหมือนกับรีจิสเตอร์หน่วยความจำอื่นๆ ที่สามารถเก็บจำนวนเต็มได้ตามใจชอบ แต่ละคำสั่งประกอบด้วยสองส่วน - รหัสการทำงานและที่อยู่ คำสั่ง PAM เป็นส่วนหนึ่งของคำสั่งภาษาแอสเซมบลี ส่วนย่อยนี้สามารถขยายได้ตามต้องการ แต่ลำดับความซับซ้อนของงานจะไม่เปลี่ยนแปลง
ตัวถูกดำเนินการสามารถเป็นหนึ่งในประเภทต่อไปนี้:
1. =ฉันหมายถึงจำนวนเต็มนั่นเอง ฉันและเรียกว่าตามตัวอักษร
2. ฉัน- ลงทะเบียนเนื้อหา ฉัน (ฉันจะต้องไม่เป็นลบ)
3. *ฉันหมายถึงการกำหนดที่อยู่ทางอ้อม นั่นคือ ค่าของตัวถูกดำเนินการคือเนื้อหาของรีจิสเตอร์ เจ,ที่ไหน เจ- จำนวนเต็มที่อยู่ในการลงทะเบียน ฉัน;ถ้า เจ<0, รถหยุด
คุณสามารถกำหนดค่าของโปรแกรมได้ รการใช้สองวัตถุ: การแมป c จากชุดของจำนวนเต็มที่ไม่เป็นลบไปยังชุดของจำนวนเต็มและ "ตัวนับคำสั่ง" ซึ่งกำหนดคำสั่งถัดไปที่จะดำเนินการ ฟังก์ชัน c คือ จอแสดงผลหน่วยความจำ,กล่าวคือ ค(ฉัน)-จำนวนเต็มอยู่ในหมายเลขทะเบียน ฉัน (เนื้อหาลงทะเบียน ฉัน).
ในตอนต้น ค(ผม)=0สำหรับทุกอย่าง ฉัน 0 ตัวนับโปรแกรมถูกตั้งค่าเป็นคำสั่งแรกใน P และเทปเอาต์พุตว่างเปล่า หลังจากการประหารชีวิต เคทีมงานจาก รตัวนับจะเปลี่ยนเป็นโดยอัตโนมัติ (เค+1)-th (นั่นคือไปยังคำสั่งถัดไป) ถ้า เค- ทีมของฉันไม่ใช่ทีมแบบ JUMP, HALT, JGTZ และอะไรทำนองนั้น
โปรแกรม RAM* อยู่ในรีจิสเตอร์หน่วยความจำ แต่ละคำสั่ง RAM* ใช้การลงทะเบียนหน่วยความจำสองครั้งติดต่อกัน: การลงทะเบียนครั้งแรกประกอบด้วยรหัสการดำเนินการ คำสั่งที่สอง - ที่อยู่ ชุดคำสั่งสำหรับ RAM* เกิดขึ้นพร้อมกับชุดคำสั่งที่สอดคล้องกันสำหรับ RAM ในทุกสิ่ง ยกเว้นการกำหนดที่อยู่ทางอ้อม ซึ่งได้รับการยกเว้น: RAM* สามารถจำลองการกำหนดที่อยู่ทางอ้อมโดยการเปลี่ยนคำสั่งระหว่างการทำงานของโปรแกรม
นอกเหนือจากการตรวจสอบว่าอัลกอริทึมที่นักเรียนนำมาใช้เป็นวิธีแก้ปัญหานั้นสามารถสร้างคำตอบที่ถูกต้องสำหรับปัญหาโดยได้รับข้อมูลเบื้องต้นบางอย่าง เมื่อตรวจสอบวิธีแก้ปัญหาแล้ว ยังคำนึงถึงเวลาการทำงานของโปรแกรมด้วย นี่ไม่ได้หมายความว่าการเขียนอัลกอริธึมที่เหมาะสมที่สุดสำหรับงานทั้งหมดโดยไม่มีข้อยกเว้นเป็นสิ่งสำคัญอย่างยิ่ง (ซึ่งมักจะใช้เวลานานในการดำเนินการและแก้ไขจุดบกพร่องอย่างมีประสิทธิภาพ) ซึ่งหมายความว่าในบางงาน พารามิเตอร์เวลาสามารถมีบทบาทสำคัญมากได้ อาจเป็นไปได้ว่าในรอบโอลิมปิกบางรอบจะไม่มีปัญหาเดียวที่จำเป็นต้องมีการปรับให้เหมาะสมที่สุด อย่างไรก็ตามสิ่งที่ตรงกันข้ามก็สามารถเกิดขึ้นได้เช่นกัน
ดังนั้นทั้งนักเรียนและครูควรจะสามารถเปรียบเทียบอัลกอริธึมต่างๆ ตามประสิทธิผลได้ เด็กนักเรียน - เพื่อเลือกวิธีที่เหมาะสมที่สุดในการแก้ปัญหาในเวลาที่เหมาะสม ครู - เพื่อเลือกงานอย่างมีประสิทธิภาพและทำความเข้าใจว่าผู้เขียนปัญหาเฉพาะมีวิธีแก้ปัญหาใดในใจเมื่อกำหนดเวลาดังกล่าวอย่างแน่นอน
ในการประเมินประสิทธิผลของอัลกอริธึม จะใช้ฟังก์ชันความซับซ้อน ซึ่งแสดงเป็น O (อ่านว่า "เกี่ยวกับใหญ่") ในความเป็นจริง ยังมีการประเมินอื่นๆ อีก แต่ในขั้นตอนที่นักเรียนเพิ่งเริ่มทำความคุ้นเคยกับอัลกอริธึมต่างๆ ก็ไม่จำเป็นจริงๆ ฟังก์ชันความซับซ้อนสะท้อนถึงรูปแบบที่เวลาการทำงานของโปรแกรมจะเพิ่มขึ้น ขึ้นอยู่กับแหล่งข้อมูลหรือปริมาณ
ตัวอย่างของอัลกอริทึมที่เวลาดำเนินการขึ้นอยู่กับข้อมูลเริ่มต้นคืออัลกอริทึมสำหรับการค้นหาตัวหารธรรมชาติทั้งหมดของตัวเลข N แน่นอนว่ายิ่งตัวเลขมากขึ้นเท่าใด ก็ยิ่งจำเป็นต้องดำเนินการวนซ้ำมากขึ้นเท่านั้น ตัวอย่างของอัลกอริธึมซึ่งเวลาในการดำเนินการขึ้นอยู่กับจำนวนข้อมูลอินพุตคือการค้นหาตัวเลขที่ใหญ่ที่สุดในอาร์เรย์ ยิ่งอาร์เรย์ยาวเท่าไร การดำเนินการเปรียบเทียบก็จะยิ่งมากขึ้นเท่านั้นเพื่อกำหนดว่าตัวเลขใดมีค่ามากที่สุด
หน้าที่หลักคือ:
l O(1) - ฟังก์ชันความซับซ้อนดังกล่าวบ่งชี้ว่าเวลารันของโปรแกรมคงที่สำหรับข้อมูลเริ่มต้นใด ๆ
l O(N) - จำนวนการดำเนินการเพิ่มขึ้นตามสัดส่วนของ N (ในที่นี้ N อาจเป็นพารามิเตอร์งานหรือจำนวนองค์ประกอบในอาร์เรย์)
ล. O(บันทึก N) - จำนวนการดำเนินการเพิ่มขึ้นตามสัดส่วนของลอการิทึมของ N (นี่คือความซับซ้อนของตัวอย่างเช่นวิธีการแบ่งครึ่งเมื่อค้นหาองค์ประกอบในอาร์เรย์ที่เรียงลำดับ) เมื่อ N เพิ่มขึ้นตามลำดับความสำคัญ จำนวนการดำเนินการจะเปลี่ยนทีละหนึ่ง โดยปกติแล้วจะไม่ระบุฐานของลอการิทึม เราสนใจธรรมชาติของการเติบโต (เร็ว/ช้า) ไม่ใช่ค่าที่แน่นอนของเวลา
l O(N2) - จำนวนการดำเนินการเพิ่มขึ้นตามสัดส่วนกำลังสองของ N โดยทั่วไปอาจเป็น O(Nk) ขึ้นอยู่กับความซับซ้อนของปัญหา
ล. O(N!) - จำนวนการดำเนินการเพิ่มขึ้นตามสัดส่วนแฟกทอเรียล N
มีรายละเอียดปลีกย่อยหลายประการที่นี่ เนื่องจากการดำเนินการบางอย่างไม่ได้ดำเนินการในระยะเวลาเท่ากัน ดังนั้น เมื่อประมาณความซับซ้อนของเวลา การดำเนินการเหล่านั้นที่ต้องใช้เวลามากที่สุดจึงถูกนำมาใช้
บ่อยครั้งที่สุดเมื่ออธิบายอัลกอริธึม การประมาณเวลาในการทำงานจะได้รับในรูปแบบที่บริสุทธิ์ กล่าวคือ โดยไม่คำนึงถึงการดำเนินการอินพุต/เอาท์พุต
ตัวอย่าง: ลองประมาณความซับซ้อนของโปรแกรมที่เข้าสู่อาร์เรย์จากคีย์บอร์ดและค้นหาองค์ประกอบที่ใหญ่ที่สุดในนั้น
ลองบวกจำนวนการดำเนินการ N+(N-1)+1=2N นั่นคือมีค่าคงที่ที่สำหรับ N ใด ๆ จำนวนการดำเนินการจะต้องไม่เกิน CN ดังนั้นความซับซ้อนของอัลกอริทึมคือ O(N)
ตัวอย่าง: ลองประมาณความซับซ้อนของโปรแกรมที่เข้าสู่อาร์เรย์จากแป้นพิมพ์และค้นหาองค์ประกอบที่มีคุณสมบัติที่กำหนดในนั้น (เช่น เท่ากับค่าที่แน่นอน)
อัลกอริทึมประกอบด้วยขั้นตอนต่อไปนี้:
การป้อนอาร์เรย์ (การดำเนินการอินพุต N) ค้นหาองค์ประกอบที่มีคุณสมบัติที่กำหนด (โชคดีแค่ไหน: องค์ประกอบสามารถตั้งอยู่ใกล้กับจุดเริ่มต้นของอาร์เรย์หรือที่ส่วนท้ายสุด หากไม่มีองค์ประกอบอยู่ คุณจะต้อง ทำการเปรียบเทียบ N ทั้งหมดเพื่อให้แน่ใจว่าสิ่งนี้) แสดงผลผลลัพธ์
ในกรณีที่ดีที่สุด อัลกอริธึมนี้จะต้องใช้การดำเนินการ N+2 (อินพุตของอาร์เรย์ทั้งหมด การเปรียบเทียบครั้งเดียว เอาต์พุต) ในกรณีที่แย่ที่สุด (เมื่อไม่มีองค์ประกอบดังกล่าว - การดำเนินการ 2N+1) ถ้า N เป็นตัวเลขจำนวนมาก เช่น ประมาณ 106 ความสามัคคีก็อาจถูกละเลยได้ ดังนั้นความซับซ้อนของอัลกอริทึมคือ O(N)
ตัวอย่าง: ลองพิจารณาฟังก์ชันความซับซ้อนของอัลกอริธึมการเข้ารหัสคำที่มีความยาว L โดยใช้วิธีทดแทน ให้มีตารางซึ่งสำหรับอักขระแต่ละตัวของตัวอักษรจะต้องเขียนอักขระที่ต้องแทนที่ ให้เราแสดงจำนวนตัวอักษรของตัวอักษร S
อัลกอริทึมประกอบด้วยขั้นตอนต่อไปนี้:
การป้อนคำ (การดำเนินการครั้งเดียว) วนซ้ำอักขระทั้งหมด
1. สำหรับอักขระแต่ละตัวให้ค้นหาการแทนที่ในตาราง (หากตารางไม่ได้เรียงลำดับและไม่มีคุณสมบัติใด ๆ ที่อำนวยความสะดวกในการค้นหาในกรณีที่เลวร้ายที่สุดจะมีการดำเนินการ S สำหรับอักขระหนึ่งตัวหากองค์ประกอบที่ค้นหาอยู่ที่จุดต่ำสุด จบ)
2. ผลลัพธ์ของสัญลักษณ์ที่พบ
สิ้นสุดรอบ
จำนวนการดำเนินการทั้งหมดคือ 1+(S+1)*L ในกรณีที่สามารถละเลยหน่วย S และ L ที่มีขนาดใหญ่เพียงพอได้ ปรากฎว่าฟังก์ชันความซับซ้อนของอัลกอริทึมนี้คือ O(S*L)
ตัวอย่าง: เรามานิยามฟังก์ชันความซับซ้อนของอัลกอริทึมในการแปลงจำนวนธรรมชาติ N เป็นระบบเลขฐานสอง (โดยไม่ต้องป้อนข้อมูลและส่งออกข้อมูล)
อัลกอริทึมประกอบด้วยขั้นตอนต่อไปนี้:
วนซ้ำจนกว่าผลลัพธ์ของการหารตัวเลขด้วย 2 จะเป็น 0
1. หารตัวเลขด้วย 2 แล้วจำเศษที่เหลือ
2. นำผลการหารมาเป็นเลขใหม่
สิ้นสุดรอบ
จำนวนการดำเนินการทั้งหมดไม่เกิน 1+log2N ดังนั้นอัลกอริทึมนี้จึงมีความซับซ้อนเท่ากับ O(log N)
หากโปรแกรมประกอบด้วยหลายส่วนซึ่งมีฟังก์ชันความซับซ้อนต่างกัน โอฟังก์ชันความซับซ้อนที่มากขึ้นจะ "ดูดซับ" ฟังก์ชันที่เล็กกว่า ตัวอย่างเช่น หากคุณทำอินพุตอาร์เรย์ O(N), การเรียงลำดับ O(N2) และเอาต์พุต O(N) ของอาร์เรย์ที่ได้รับการจัดลำดับ คุณสามารถพูดได้ว่าโปรแกรมทั้งหมดมีความซับซ้อน O(N2)
การประยุกต์ใช้ความรู้เชิงปฏิบัติเกี่ยวกับฟังก์ชันความซับซ้อนของอัลกอริทึมนั้นมีสองเท่า ประการแรก สำหรับงานบางงาน สามารถเลือกอัลกอริธึมที่เหมาะสมกว่าได้หากมีข้อมูลที่เกี่ยวข้องในวรรณกรรม ประการที่สอง เมื่อทราบเวลาการทำงานของโซลูชันของเขากับข้อมูลเริ่มต้นชุดเดียว นักเรียนสามารถประมาณเวลาการทำงานของโปรแกรมเดียวกันโดยประมาณจากข้อมูลที่สอดคล้องกับข้อจำกัดสูงสุดสำหรับปัญหาที่กำหนด
คำถาม
งานเหล่านี้ใช้สำหรับการทดสอบตัวเองกับวัสดุที่นำเสนอและไม่ได้บังคับ
1. กำหนดฟังก์ชันความซับซ้อนของอัลกอริทึมสำหรับการแก้สมการกำลังสอง
2. กำหนดฟังก์ชันความซับซ้อนของอัลกอริทึมสำหรับการวาดรูปหลายเหลี่ยมปกติตามจำนวนด้านที่กำหนด
3. กำหนดฟังก์ชันความซับซ้อนของอัลกอริธึมสำหรับการแทรกองค์ประกอบลงในอาร์เรย์ที่ตำแหน่งที่กำหนด (ด้วยการเปลี่ยนแปลงเบื้องต้นขององค์ประกอบทั้งหมดที่มีตัวเลขมากกว่าหรือเท่ากับตำแหน่งที่กำหนดทีละตำแหน่งไปทางขวา)
4. กำหนดฟังก์ชันความซับซ้อนของอัลกอริทึมในการบวกตัวเลขธรรมชาติสองตัวลงในคอลัมน์ (ให้ A เป็นจำนวนหลักของตัวเลขแรก, B คือจำนวนหลักของวินาที)
5. กำหนดฟังก์ชันความซับซ้อนของอัลกอริทึมสำหรับการคูณจำนวนธรรมชาติสองตัวในคอลัมน์