Iteration vs Recursion: เข้าใจต่างกันอย่างไร และเลือกใช้อย่างไร
บทนี้สรุปครบสำหรับผู้เริ่มต้น: แนวคิดพื้นฐาน, ตัวอย่างโจทย์เดียวกันที่แก้ได้ 2 แบบ, การวิเคราะห์ Time/Space, เปรียบเทียบ readability/performance/memory, ข้อผิดพลาดที่พบบ่อย, ตารางสรุป และแบบฝึกหัดพร้อมแนวเฉลย
1. Iteration คืออะไร
Iteration คือการทำงานซ้ำด้วยลูป เช่น for, while, do...whileโดยอัปเดตสถานะทีละรอบจนเงื่อนไขหยุดเป็นจริง
ภาพจำง่าย: เดินทีละก้าวในทางตรง ทุกก้าวอยู่ในฟังก์ชันเดิม ไม่เรียกชั้นใหม่
2. Recursion คืออะไร
Recursion คือการที่ฟังก์ชันเรียกตัวเองเพื่อแก้ปัญหาเดิมในขนาดที่เล็กลง จนถึงจุดหยุดที่เรียกว่า base case
เมื่อถึง base case แล้ว ระบบจะค่อย ๆ คืนค่ากลับทีละชั้นผ่าน call stack
3. ความต่างหลักระหว่างสองแนวคิด
ตารางภาพสรุปข้อแตกต่างหลัก
| ประเด็น | Iteration | Recursion |
|---|---|---|
| รูปแบบคิด | ทำงานซ้ำด้วยลูปจนจบเงื่อนไข | ย่อยปัญหาแล้วเรียกฟังก์ชันตัวเอง |
| จุดหยุด | หยุดเมื่อเงื่อนไขลูปเป็นเท็จ | หยุดเมื่อเจอ base case |
| Time Complexity (factorial) | O(n) | O(n) |
| Space Complexity (factorial) | O(1) | O(n) เพราะ call stack |
| Readability | คุ้นเคยสำหรับมือใหม่ส่วนใหญ่ | อ่านง่ายมากเมื่อปัญหามีโครงสร้างซ้อน |
| ความเสี่ยง | เสี่ยง loop ไม่จบถ้าเงื่อนไขผิด | เสี่ยง stack overflow ถ้าลึกเกินไป |
4. ตัวอย่างโจทย์เดียวกัน: factorial(n)
โจทย์: รับจำนวนเต็มไม่ติดลบ n แล้วคืนค่า n!โดยนิยามคือ n! = n * (n - 1) * ... * 1 และ 0! = 1
เราจะใช้โจทย์เดียวกันนี้แก้ทั้งแบบ loop (iteration) และแบบเรียกซ้ำ (recursion)
Iteration (Loop State)
ทำงานรอบเดียวต่อครั้ง อัปเดตตัวแปรเดิม แล้วเดินต่อ
Recursion (Call Stack)
เรียกฟังก์ชันซ้อนขึ้นไปบน stack แล้วค่อยคืนค่ากลับลงมา
5. วิธีแก้แบบ iteration
- - เริ่มตัวแปรสะสมผลลัพธ์ที่ 1
- - วนลูปจาก 2 ถึง n แล้วคูณสะสมไปเรื่อย ๆ
- - เมื่อจบลูป คืนค่าผลลัพธ์
6. วิธีแก้แบบ recursion
- - กำหนด base case: ถ้า n <= 1 ให้คืน 1
- - กำหนด recursive case: factorial(n) = n * factorial(n - 1)
- - ทุกครั้งต้องลดขนาดปัญหา เพื่อให้เข้าใกล้ base case
7. ตัวอย่างโค้ด JavaScript ทั้งสองแบบ
ใช้ loop เดินทีละรอบและคูณสะสม
ใช้ base case + recursive case
8. เปรียบเทียบ Time Complexity
สำหรับ factorial ทั้ง iteration และ recursion ต้องคำนวณประมาณ n ขั้นเหมือนกัน ดังนั้น Time Complexity = O(n) ทั้งคู่
สรุปสั้น: ในโจทย์นี้ ความเร็วเชิงการเติบโตใกล้กัน แต่ต่างกันที่การใช้หน่วยความจำ
9. เปรียบเทียบ Space Complexity
Iteration ใช้ตัวแปรไม่กี่ตัวและไม่เพิ่มชั้นเรียกฟังก์ชันตาม n จึงเป็น O(1)
Recursion สร้าง call stack ตามความลึกของการเรียกซ้ำ จึงเป็น O(n)และหากลึกเกินไปอาจเกิด stack overflow
10. เปรียบเทียบความอ่านง่ายและความเหมาะสม
Readability: iteration มักเข้าใจง่ายในงานวนลูปทั่วไป ส่วน recursion จะอ่านลื่นกว่าถ้าปัญหามีโครงสร้างเป็นชั้นหรือแตกแขนง
Performance: หลายกรณี iteration มี overhead น้อยกว่าเพราะไม่ push/pop stack
Memory usage: iteration มักประหยัดกว่า recursion ในงานลูปตรง ๆ
ภาพแสดง iteration เดินทีละรอบ กับ recursion เรียกซ้อนทีละชั้น
ฝั่งซ้ายคือเดินหน้าเป็นรอบ ๆ ส่วนฝั่งขวาคือซ้อน call ให้ลึกขึ้นก่อน แล้วค่อยคืนค่ากลับ
11. กรณีที่ iteration เหมาะกว่า
- - งานที่ต้องวนข้อมูลปริมาณมากแบบเส้นตรง เช่น รวมค่าในอาเรย์ยาว
- - งานที่เน้นใช้หน่วยความจำต่ำและหลีกเลี่ยง call stack ลึก
- - งานที่ทีมคุ้นกับโค้ดเชิงลูปและต้องการ debug ทีละรอบง่าย
12. กรณีที่ recursion เหมาะกว่า
- - งานที่ปัญหาแบ่งย่อยซ้ำโครงสร้างเดิม เช่น factorial, fibonacci, divide and conquer
- - งานเดินโครงสร้างต้นไม้/กราฟ เช่น DFS traversal
- - งาน backtracking ที่ต้องลองทางเลือกและย้อนกลับทีละชั้น
13. ข้อผิดพลาดที่พบบ่อย
- - ลืม base case ทำให้ recursion ไม่หยุด
- - recursive case ไม่ได้ลดขนาดปัญหา เช่น เรียกด้วยค่าเดิม
- - ตั้งค่าเริ่มต้นลูปผิด เช่น i = 0 กับ factorial ทำให้ผลลัพธ์เป็น 0
- - เงื่อนไขลูปผิดพลาด เช่น i < n แทน i <= n ในโจทย์ที่ต้องรวมถึง n
- - ไม่จัดการ input ผิดรูปแบบ เช่น n ติดลบ หรือไม่ใช่จำนวนเต็ม
14. ตารางสรุปเปรียบเทียบ Iteration vs Recursion
ตารางภาพสรุปข้อแตกต่างหลัก
| ประเด็น | Iteration | Recursion |
|---|---|---|
| รูปแบบคิด | ทำงานซ้ำด้วยลูปจนจบเงื่อนไข | ย่อยปัญหาแล้วเรียกฟังก์ชันตัวเอง |
| จุดหยุด | หยุดเมื่อเงื่อนไขลูปเป็นเท็จ | หยุดเมื่อเจอ base case |
| Time Complexity (factorial) | O(n) | O(n) |
| Space Complexity (factorial) | O(1) | O(n) เพราะ call stack |
| Readability | คุ้นเคยสำหรับมือใหม่ส่วนใหญ่ | อ่านง่ายมากเมื่อปัญหามีโครงสร้างซ้อน |
| ความเสี่ยง | เสี่ยง loop ไม่จบถ้าเงื่อนไขผิด | เสี่ยง stack overflow ถ้าลึกเกินไป |
15. สรุปท้ายบทแบบจำง่าย
- - Iteration = วนลูปทีละรอบในฟังก์ชันเดิม
- - Recursion = เรียกตัวเองเป็นชั้น ๆ จนถึง base case
- - โจทย์เดียวกันอาจได้ Time เท่ากัน แต่ Space ต่างกัน
- - งานข้อมูลใหญ่เชิงเส้นมักเริ่มที่ iteration ก่อน
- - งานโครงสร้างซ้อน เช่น ต้นไม้ มักเหมาะกับ recursion
16. แบบฝึกหัด 4 ข้อ (Iteration vs Recursion Lab style)
ในหัวข้อนี้เราเปลี่ยนแบบฝึกหัดเป็น Lab ของตัวเองบน Playground เพื่อให้ลงมือทำจริงครบ 4 ข้อและมี runtime checks อัตโนมัติทุกข้อ
- - Lab 01: เขียน factorial แบบ iteration
- - Lab 02: เขียน factorial แบบ recursion
- - Lab 03: sumTo แบบ iteration และ recursion ต้องให้ผลเท่ากัน
- - Lab 04: findMax แบบ iteration และ recursion ต้องให้ผลเท่ากัน