บทที่ 1: การวิเคราะห์อัลกอริทึม (Algorithm Analysis)

วัตถุประสงค์
การเรียกซ้ำ (Recursion)

ก่อนที่จะวิเคราะห์อัลกอริทึม เราควรต้องทำความเข้าใจกับการทำงานแบบซ้ำๆของโปรแกรม เพื่อให้เข้าใจหลักการทำงานและเวลาที่ใช้ในการประมวลผล ตัวอย่างที่เห็นเด่นชัดคือฟังก์ชันเรียกซ้ำหรือรีเคอชัน ซึ่งเป็นฟังก์ชันที่เรียกตัวเองซ้ำๆจนกว่าจะทำงานเสร็จ โดยหลักการของรีเคอชันคือการแยกปัญหาที่ซับซ้อนให้เป็นปัญหาที่ง่ายขึ้นในทุกๆครั้งที่เรียกใช้ฟังก์ชันจนกระทั่งถึงจุดที่ง่ายที่สุดหรือจุดสุดท้ายในการทำงานของรีเคอชันที่เราเรียกว่าจุดสิ้นสุดการเรียกฟังก์ชันหรือ base case หลังจากนั้นฟังก์ชันก็จะทะยอยส่งผลลัพธ์กลับคืนไปยังการเรียกใช้ฟังก์ชันก่อนหน้า จนไปถึงการเรียกใช้ฟังก์ชันในครั้งแรก ก็จะได้ผลลัพธ์สุดท้ายที่สมบูรณ์ ตัวอย่างการทำงานแบบรีเคอชัน เช่นการคำนวณหาค่าของแฟคทอเรียล N! หรือ fac(N) ซึ่งเป็นการคูณเลขเริ่มจาก N และลดทีละ 1 จนถึง 1 คือ N*(N-1)*(N-2)*…*3*2*1 ซึ่งจะเห็นว่าการคูณด้วย 1 เป็นขั้นตอนสุดท้ายในการเรียกใช้ฟังก์ชันเมื่อตัวคูณเป็น 1 หรือเป็น base case ซึ่งไม่มีการเรียกซ้ำอีก และจะส่งคืนผลลัพธ์เป็น 1 ไปยังการเรียกใช้ฟังก์ชันก่อนหน้า จนย้อนกลับไปถึง N ที่มีการเรียกใช้ฟังก์ชันครั้งแรก เช่นถ้า N = 5 จะมีการเรียกใช้ฟังก์ชันซ้ำเป็น fac(5)*fac(4)*fac(3)*fac(2)*fac(1) จะได้ผลลัพธ์ที่ส่งคืนมาเป็น 1*2*3*4*5 = 120 เป็นต้น ตัวอย่างฟังก์ชันรีเคอชันในภาษา C# แสดงได้ตามรูปที่ 1.1 โดยที่จำนวนครั้งในการเรียกใช้ฟังก์ชันจนทำงานเสร็จได้ผลลัพธ์คือ N ครั้ง หรือเราอาจจเรียกว่าฟังก์ชันที่มีประสิทธิภาพหรือเวลาการทำงานแบบเชิงเส้น (Linear) ระดับ N (Order N) คือเท่ากับจำนวนอินพุท N นั่นเอง


Placeholder image

การวิเคราะห์อัลกอริทึม (Algorithm Analysis) -->> Top

เมื่อเราเข้าใจเรื่องเวลาในการทำงานของฟังก์ชันหรือต่อไปเราจะเรียกว่าอัลกอริทึม เราก็สามารถวิเคราะห์การทำงานของฟังก์ชันหรืออัลกอริทึมว่ามีประสิทธิภาพดีหรือไม่ดีอย่างไร โดยวัดเวลาในการทำงาน (Running Time) ของแต่ละอัลกอริทึมหรือการวิเคราะห์อัลกอริทึม (Algorithm Analysis)

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

มีปัจจัยหลายอย่างที่ช่วยให้โปรแกรมหรืออัลกอริทึมทำงานได้รวดเร็ว 1. เช่นความเร็วของเครื่องคอมพิวเตอร์ (เช่น CPU, RAM, DISK) 2. ประสิทธิภาพของตัวแปลภาษา (Compiler) 3. ประสิทธิภาพโปรแกรมที่ใช้สร้างอัลกอริทึม (Programmig Language) และ 4. หลักการทำงานพื้นฐานของอัลกอริทึม ซึ่งปัจจัยที่ 1,2,3 เราสามารถกำหนดให้เหมือนกันได้ ดังนั้นปัจจัยที่ 4 จึงเป็นปัจจัยหลักที่จะบอกว่าอัลกอรึทึมแบบไหนดีกว่ากัน ในการแก้ปัญหาเดียวกัน ซึ่งเราจะใช้ฟังก์ชัน Big O ในการประมาณหรือวัดการทำงานของอัลกอริทึม


Big O

Big O Notation คือเวลาในการทำงานที่ไม่เกินเวลาที่นานที่สุดที่คอมพิวเตอร์ต้องใช้ในการทำงานของอัลกอริทึมใดๆ (less that or equal to upper bound)

ยกตัวอย่าง เช่นการค้นหาข้อมูลประชากรแบบลำดับ (Sequential) จากจำนวนประชากร 70 ล้านคน เวลานานที่สุดที่ใช้ในการค้นหาคือ 70 ล้านครั้ง หรือ N ถ้าคนที่เราต้องการค้นหาอยู่ที่ลำดับสุดท้าย แต่ถ้าบังเอิญคนนั้นอยู่ที่ลำดับแรก เวลาก็จะเป็น 1 ครั้ง หรือถ้าอยู่ตรงกลาง เวลาก็จะเป็น 35 ล้านครั้ง ซึ่งจะเห็นว่าเวลาที่ใช้จะน้อยกว่าหรือเท่ากับ N เสมอ อันนี้คือตัวอย่างของฟังก์ชัน Big O ของ N หรือที่เราเรียกว่า O(N) เวลาที่มากที่สุดที่ใช้จะเกิดในกรณีที่เรียกว่า Worst Case ส่วนเวลาที่น้อยที่สุดเราเรียกว่า Best Case และเวลาเฉลี่ยเราเรียกว่า Average Case แต่โดยรวมๆ สรุปได้ว่าเวลาที่ใช้ก็คือ O(N) ซึ่งจะไม่เกิน N นั่นเอง อันนี้คือเราเรียกว่าการวิเคราะห์อัลกอริทึมด้วย Big O ซึ่งสามารถบอกได้โดยประมาณว่าอัลกอริทึมแบบไหนดีกว่ากัน เช่นการค้นหาข้อมูลแบบลำดับ ใช้เวลา O(N) ส่วนการค้นหาข้อมูลแบบไบนารี (Binary Search) ใช้เวลา O(log N) ซึ่งจะเร็วกว่าและดีกว่าแบบลำดับ

ฟังก์ชัน Big O -->> Top

เราแบ่งชนิดของฟังก์ชัน ตามปริมาณข้อมูล N ได้เป็นฟังก์ชัน Big O ต่อไปนี้
1. One or Constant: O(1) หรือ O(C) ทำงานไม่เกิน 1 ครั้ง เช่นการค้นหาข้อมูลด้วยตารางแฮช (Hash Table) หรือบางครั้งก็ไม่เกิน C ครั้ง เช่นการค้นหาข้อมูลด้วยตารางแฮช ในกรณีที่มีการชนกันของค่าแฮช
2. Logarithm: O(log N) ทำงานไม่เกิน log N ครั้ง เช่นการค้นหาข้อมูลแบบไบนารี
3. Linear: O(N) ทำงานไม่เกิน N ครั้ง เช่นการค้นหาข้อมูลแบบลำดับ
4. N Logarithm: O(N log N) ทำงานไม่เกิน N log N ครั้ง เช่นการเรียงข้อมูลแบบแบ่งแล้วรวม (Merge Sort)
5. Quadratic: O(N2) ทำงานไม่เกิน N2 ครั้งเช่นการเรียงข้อมูลแบบบับเบิล (Bubble Sort)
6. Cubic: O(N3) ทำงานไม่เกิน N3 ครั้ง เช่นการทำงานของฟังก์ชันโพลีโนเมียล N3 หรือฟังก์ชันที่มีลูปซ้อนกัน 3 ชั้น
7. Exponential: O(2N) ทำงานไม่เกิน 2N ครั้ง เช่นการย้ายจานในปัญหาหอคอยฮานอย (Tower of Hanoi)

สำหรับตัวอย่างของแต่ละฟังก์ชัน Big O ก็จะมีการกล่าวถึงในบทที่เกี่ยวข้องกับเรื่องนั้นๆ
ตารางที่ 1.1 สรุปจำนวนครั้งและเวลาสูงสุดในการทำงานของแต่ละฟังก์ชัน Big O ถ้าการทำงาน1 ครั้ง ใช้เวลาประมาณ 1 วินาที โดยที่ s คือวินาที m คือนาที h คือชั่วโมง d คือวัน y คือปี my คือล้านปี และ *** คือมันนานมากจนบอกเป็นตัวเลขไม่ได้
ตารางที่ 1.1 เวลาสูงสุดในการทำงานของฟังก์ชัน Big O 


Placeholder image

  • จากตารางจะเห็นว่า แค่จำนวนข้อมูลเพียง 64 ตัว อัลกอริทึมแบบ O(2N) ต้องใช้เวลาในการทำงานนานถึงห้าแสนล้านปี (500,000,000,000 ปี) ดังนั้นคงไม่มีใครสามารถย้ายจาน 32 ใบ ในปัญหาหอคอยฮานอยได้สำเร็จได้ในชั่วชีวิต ซึ่งต้องใช้เวลาประมาณ 136 ปี
  • พักให้คิด
  • อีกตัวอย่างที่คล้ายกับความยาวนานของปัญหาแบบ 2N คือการหาความสูงจากการพับกระดาษที่มีความหนา 0.1 มม. และกระดาษมีความยาวไม่จำกัด โดยพับทีละครึ่ง เป็นจำนวน 40 ครั้ง (โดยสมมติว่าพับได้) จะได้ความสูงของกระดาษเป็นเท่าไร มากกว่าหรือน้อยกว่าความสูงของตึก 30 ชั้น ซึ่งจะมีความสูงประมาณ 100 เมตร
  • ปัญหาหอคอยฮานอย (Tower of Hanoi) -->> Top
  • นิยามปัญหาหอคอยฮานอยคือ มีเสา 3 ต้น (1,2,3) และจาน N ใบ ตามรูป 1.2 โดยที่จานแต่ละใบมีขนาดไม่เท่ากัน และจานใบเล็กต้องวางไว้บนจานใบใหญ่กว่าเท่านั้น โดยจานทั้งหมด N ใบ จะวางไว้ที่เสาที่ 1 จากนั้นให้ทำการย้ายจานทั้งหมดจากเสาที่ 1 ไปยังเสาที่ 3 โดยใช้เสาที่ 2 เป็นเสาช่วย และมีเงื่อนไขว่าในแต่ละครั้งจะย้ายจานได้เพียงใบเดียว และจานใบเล็กต้องวางไว้บนจานใบที่ใหญ่กว่าเสมอ ยกตัวอย่างในกรณีที่มีจาน 3 ใบ ต้องทำการเคลื่อนย้ายจานทั้งหมดกี่ครั้ง

  • Placeholder image

    หลักคิด / อัลกอริทึม

    ขั้นตอน 1-3 แสดงได้ตามรูปที่ 1.3 ส่วนรายละเอียดขั้นตอนทั้งหมดในการย้ายจาน 3 ใบ แสดงได้ตามรูปที่ 1.4 Pseudocode ในการย้ายจาน แสดงได้ตามรูปที่ 1.5 ฟังก์ชันรีเคอชัน Hanoi( ) แสดงได้ตามรูปที่ 1.6 รายละเอียดฟังก์ชัน Hanoi( ) และการเรียกใช้งาน แสดงได้ตามรูปที่ 1.7 โปรแกรมทดสอบการแก้ปัญหาหอคอยฮานอย แสดงได้ตามรูปที่ 1.8 โปรแกรมกราฟิกส์จำลองการทำงานของการแก้ปัญหาหอคอยฮานอย แสดงได้ตามรูปที่ 1.9


    อัลกอริทึมในการย้ายจาน -->> Top

    Placeholder image

    รายละเอียดขั้นตอนการย้ายจาน

    Placeholder image

    ในการย้ายจาน

    Placeholder image

    Placeholder image
    Function Honoi() -->> Top
    Function Honoi()
    Placeholder image
    Test program
    Placeholder image

    Placeholder image

    วิธีการวัดเวลา (Running Time Calculation) -->> Top

    ตัวอย่างโปรแกรมวัดเวลาในการแสดงข้อมูลตัวเลข 1,000 ตัวบนหน้าจอ
    Placeholder image
    ผลลัพธฺแสดงการพิมพ์ข้อมูลตัวเลข 1,000 ตัวและแสดงเวลาที่ใช้ 97.9699 มิลลิวินาที

    Placeholder image

    การหาค่า Big O ของฟังก์ชัน -->> Top

    ฟังก์ชัน Big O จะเลือกเฉพาะเทอมที่มีความสำคัญสูงสุดเท่านั้น และตัดตัวคูณที่เป็นค่าคงที่ออกไป เช่น 12N2 + 3N คือ O(N2) หรือ (N3 + 3N2 + 2N)/6 คือ O(N3) เป็นต้น

    กราฟของฟังก์ชัน Big O -->> Top
    กราฟของฟังก์ชัน Big O

    กราฟด้านล่างในรูปที่ 1.10 และ 1.11 แสดงอัตราการเพิ่มขึ้นของระยะเวลาในการทำงาน เทียบกับจำนวนข้อมูลอินพุทที่ใช้ของฟังก์ชัน Big O แต่ละแบบ โดยแกน x คือจำนวนข้อมูลอินพุท และแกน y คือ ระยะเวลาที่ใช้

    Placeholder image
    รูปที่ 1.10 กราฟแสดงเวลาในการทำงานของฟังก์ชัน Big O เมื่อ N มีค่าน้อย

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

    Placeholder image
    รูปที่ 1.11 กราฟแสดงเวลาในการทำงานของฟังก์ชัน Big O เมื่อ N มีค่ามาก

    จากกราฟในรูปที่ 1.11 จะเห็นว่าถ้าจำนวนข้อมูลอินพุท N มากขึ้น หลายๆฟังก์ชันจะคำนวณเวลาไม่ได้ แต่บางฟังก์ชันเช่น O(N) ยังสามารถคำนวณเวลาในการทำงานได้เสมอ

    ตัวอย่างโปรแกรม -->> Top
    ตัวอย่างที่ 1: โปรแกรม Factorial

    2020. mnet50.com