Space สำคัญกว่า → เลือก Iteration
ถ้าเน้นประหยัดหน่วยความจำ — Iteration ใช้ O(1) ส่วน Recursion ใช้ O(n) — ต่างกันมหาศาลกับ n ขนาดใหญ่
Iteration & Recursion
เปรียบเทียบ Iteration และ Recursion ในโจทย์เดียวกัน — Time/Space Complexity, Readability, Stack Overflow และหลักเลือกใช้ในงานจริง
Iteration และ Recursion เป็นสองวิธีคิดในการแก้ปัญหาแบบทำซ้ำ — ต่างกันที่ "ควบคุมการวนซ้ำอย่างไร" และ "จัดการ state ไว้ที่ไหน"
| ประเด็น | Iteration | Recursion |
|---|---|---|
| รูปแบบคิด | ทำงานซ้ำด้วยลูปจนจบเงื่อนไข | ย่อยปัญหาแล้วเรียกฟังก์ชันตัวเอง |
| จุดหยุด | หยุดเมื่อเงื่อนไขลูปเป็นเท็จ | หยุดเมื่อเจอ base case |
| State อยู่ที่ไหน | ตัวแปรใน scope เดียว | Call stack — แต่ละชั้นมีตัวแปรของตัวเอง |
| Time Complexity (factorial) | O(n) | O(n) |
| Space Complexity (factorial) | O(1) | O(n) เพราะ call stack |
| Readability | คุ้นเคยสำหรับมือใหม่ส่วนใหญ่ | อ่านง่ายมากเมื่อปัญหามีโครงสร้างซ้อน |
| ความเสี่ยงหลัก | Infinite loop ถ้าเงื่อนไขผิด | Stack overflow ถ้าลึกเกินไป |
| เหมาะกับ | งานเชิงเส้น ปริมาณข้อมูลมาก | โครงสร้างต้นไม้ Divide & Conquer |
ใช้โจทย์ factorial(n) เป็นตัวเปรียบเทียบ — ทั้งสองแบบให้ผลลัพธ์เท่ากัน แต่แนวคิดและกลไกภายในต่างกันอย่างสิ้นเชิง
ตัวแปร result เปลี่ยนค่าทีละรอบ — ไม่เรียกฟังก์ชันเพิ่ม — O(1) space
// Iteration: ใช้ for loop คูณสะสมทีละรอบ
function factorialIteration(n) {
let result = 1;
for (let i = 2; i <= n; i += 1) {
result *= i;
}
return result;
}เรียกฟังก์ชันตัวเองซ้อนกัน n ครั้ง — O(n) space จาก call stack
// Recursion: เรียกตัวเองด้วย n-1 จนถึง base case
function factorialRecursion(n) {
if (n <= 1) {
return 1;
}
return n * factorialRecursion(n - 1);
}ทั้งสองแบบใช้ Time O(n) เท่ากัน — แต่ Recursion ใช้ Space O(n) ในขณะที่ Iteration ใช้แค่ O(1)
เวลาทำงาน Iteration กับการ Recursion — สิ่งที่เกิดขึ้นเบื้องหลังต่างกันโดยสิ้นเชิง
Iteration (Loop State): ตัวแปร result ถูกอัปเดตทับค่าเดิมไปเรื่อย ๆ — แต่ละรอบมีแค่ result ตัวเดียว
Recursion (Call Stack): แต่ละครั้งที่เรียกตัวเอง จะสร้าง frame ใหม่บน stack — ถ้าเรียก factorial(5) จะมี 5 frames ซ้อนกัน
สำหรับปัญหาที่ต้องทำงาน n ครั้ง (เช่น factorial, sum, findMax) — ทั้ง Iteration และ Recursion ต่างก็ต้องทำงานประมาณ n ขั้นตอนเหมือนกัน จึงมี Time Complexity = O(n) เท่ากัน
แต่ในทางปฏิบัติ Iteration มักมี overhead น้อยกว่า — เพราะไม่ต้อง push/pop stack ทุกครั้งที่เรียกฟังก์ชัน ส่วน Recursion มีค่าใช้จ่ายแฝงจากการจัดการ call stack (function call overhead)
Recursion อาจดูเหมือน O(1) ต่อรอบ แต่ความลึกคือ n
มือใหม่บางคนเข้าใจผิดว่า recursion แต่ละชั้นทำงาน O(1) จึงคิดว่า time รวมเป็น O(1) — ในความเป็นจริงจำนวนครั้งที่เรียกซ้ำแปรผันตรงกับ n ดังนั้น Time = O(n) เสมอ
นี่คือข้อแตกต่างที่สำคัญที่สุดระหว่าง Iteration และ Recursion — การใช้หน่วยความจำ
Iteration ใช้ Space O(1) — ตัวแปรแค่ไม่กี่ตัวใน scope เดียว ไม่เพิ่มชั้นการเรียกใหม่ ไม่ว่าจะวนกี่รอบก็ตาม
Recursion ใช้ Space O(n) — ทุกครั้งที่เรียกตัวเอง จะเพิ่ม frame ใหม่บน call stack — ถ้า n = 10,000 จะมี 10,000 frames ซึ่งอาจทำให้เกิด Stack Overflow ได้
| ขนาด n | Iteration (Space) | Recursion (Space) | Recursion เสี่ยงไหม |
|---|---|---|---|
| 10 | O(1) | O(10) | ปลอดภัย |
| 100 | O(1) | O(100) | ปลอดภัย |
| 1,000 | O(1) | O(1,000) | ปลอดภัย |
| 10,000 | O(1) | O(10,000) | ⚠️ อาจ overflow |
| 100,000 | O(1) | O(100,000) | ❌ Stack Overflow |
⚠️ Recursion + ข้อมูลใหญ่ = ความเสี่ยง Stack Overflow
ก่อนใช้ recursion กับข้อมูลจำนวนมาก (เช่น array > 10,000 ตัว) ให้ประเมินความลึกของการเรียกซ้ำก่อน — ถ้าลึกเกินไปให้ใช้ iteration หรือใช้เทคนิค trampoline/tail recursion เพื่อหลีกเลี่ยง stack overflow
หลักตัดสินใจว่าจะเลือกใช้แบบไหนในสถานการณ์ต่าง ๆ
ถ้าเน้นประหยัดหน่วยความจำ — Iteration ใช้ O(1) ส่วน Recursion ใช้ O(n) — ต่างกันมหาศาลกับ n ขนาดใหญ่
ปัญหาเกี่ยวกับต้นไม้และกราฟ (DFS, การ traverse) มักเขียนแบบ recursion ได้สวยและตรงไปตรงมากว่า
ถ้าไม่มั่นใจว่าความลึกไม่เกินขีดจำกัด call stack → ใช้ Iteration หรือ refactor เป็น iterative stack simulation
ในกรณีที่ time complexity เท่ากัน ให้เลือกวิธีที่ทีมอ่านเข้าใจง่ายกว่า — โค้ดที่ maintain ได้ดีกว่ามีค่ามากกว่า optimisation เล็กน้อย
หลายโปรเจกต์ใช้ทั้ง Iteration และ Recursion ปนกัน — เลือกตามแต่ละฟีเจอร์ ไม่ต้องยึดติดวิธีเดียวทั้งโปรเจกต์
ทดสอบความเข้าใจก่อนเริ่มทำแบบฝึกหัด
คิดถึงจำนวนตัวแปรที่ใช้ในฟังก์ชัน
2 ข้อนี้จะให้คุณเขียนฟังก์ชันเดียวกันด้วยทั้ง Iteration และ Recursion — เพื่อสัมผัสความต่างของแนวคิดทั้งสองโดยตรง