ประสิทธิภาพด้านเวลาของโปรแกรมตามอัลกอริธึมที่เกี่ยวข้อง แนวคิดเกี่ยวกับความซับซ้อนและประสิทธิภาพของอัลกอริทึมและโครงสร้างข้อมูล เราจะทำอย่างไรกับวัสดุที่ได้รับ?

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

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

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

พื้นหลัง

ความสำคัญของประสิทธิภาพโดยเน้นที่เวลาดำเนินการได้รับการเน้นย้ำโดย 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. การวิเคราะห์ความเข้มข้นของแรงงานทีละบรรทัดสำหรับการปฏิบัติงานด้วยภาษาพื้นฐานหมายถึงการวิเคราะห์แบบสะสม (โดยคำนึงถึงการปฏิบัติงานทั้งหมด) หรือการวิเคราะห์การปฏิบัติงาน (โดยคำนึงถึงความซับซ้อนของการปฏิบัติงานแต่ละครั้ง)
  3. องค์ประกอบผกผันของฟังก์ชันความซับซ้อนตามวิธีการวิเคราะห์โครงสร้างอัลกอริทึมพื้นฐานสำหรับกรณีที่ดีที่สุด ค่าเฉลี่ย และแย่ที่สุด

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


ห้องสมุดของโปรแกรมเมอร์


“หากการดีบักเป็นกระบวนการกำจัดข้อผิดพลาด การเขียนโปรแกรมควรเป็นกระบวนการแนะนำข้อผิดพลาดเหล่านั้น”

อี. ไดจค์สตรา

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. กำหนดฟังก์ชันความซับซ้อนของอัลกอริทึมสำหรับการคูณจำนวนธรรมชาติสองตัวในคอลัมน์