จาก Brute Force สู่ Optimal: ยกระดับโค้ดให้มีประสิทธิภาพแบบมือโปร

7 นาที 15 views บันทึกเป็น PDF
จาก Brute Force สู่ Optimal: ยกระดับโค้ดให้มีประสิทธิภาพแบบมือโปร

เลิกเขียนโค้ดแบบวนลูปซ้อนกันจนโปรแกรมค้าง เรียนรู้วิธีคิดแบบ Optimal ผ่าน Kadane's Algorithm เพื่อเปลี่ยนโค้ดมือใหม่ให้ทำงานเร็วขึ้นระดับ Jedi

จุดเริ่มต้นของการเดินทาง: ทำไมต้องเปลี่ยนวิธีคิด?

ผมยังจำความรู้สึกครั้งแรกที่ต้องแก้โจทย์ Maximum Subarray Sum (หาผลรวมที่มากที่สุดของส่วนย่อยในอาเรย์) ในการสัมภาษณ์งานโปรแกรมเมอร์ได้ดี ตอนนั้นผมมองไปที่ชุดข้อมูลแล้วคิดแค่ว่า "ก็แค่เช็คทุกความเป็นไปได้สิ" แล้วผมก็เริ่มเขียนลูปซ้อนลูปเหมือนกำลังสร้างป้อมปราการป้องกันตัวเองจากความผิดพลาด

การเขียนลูปซ้อนกันสามชั้นทำให้โค้ดของผมมีค่า O(n³) ซึ่งในเชิงวิทยาการคอมพิวเตอร์ถือว่าช้ามากเหมือนเต่าคลาน สมองผมตอนนั้นรู้สึกเหมือนโดนปืนเลเซอร์ยิงใส่เพราะมั่นใจเหลือเกินว่ามันใช้งานได้จริง แต่ความเป็นจริงคือเมื่อผู้สัมภาษณ์โยนข้อมูลขนาด 10⁵ (หนึ่งแสน) ตัวเข้ามา โปรแกรมของผมก็ค้างสนิททันที

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

ถอดรหัส Brute Force: ทำไมวิธีเดิมถึงพัง?

ก่อนจะเก่งขึ้น เราต้องเข้าใจก่อนว่าทำไมวิธีที่เราคุ้นเคยถึงเป็นอุปสรรค Brute Force คือการทำสิ่งเดิมซ้ำๆ โดยไม่จดจำข้อมูลที่เคยคำนวณไปแล้ว เปรียบเสมือนการที่คุณต้องคำนวณเลข 1+2+3+4+5 แล้วพอจะหาค่า 1+2+3+4+5+6 คุณกลับเริ่มบวกใหม่ตั้งแต่เลข 1 แทนที่จะเอาผลลัพธ์เดิมมาบวกแค่ 6

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

def max_subarray_bruteforce(arr):
    n = len(arr)
    best = float('-inf') # ตั้งค่าเริ่มต้นให้ต่ำที่สุด
    for i in range(n):
        for j in range(i, n):
            # คำนวณผลรวมใหม่ทุกครั้งในลูปนี้ (ส่วนที่ทำให้ช้า)
            s = sum(arr[i:j+1]) 
            if s > best:
                best = s
    return best

ในโค้ดด้านบน การใช้ sum(arr[i:j+1]) ภายในลูปสองชั้นคือสาเหตุหลักของปัญหา เพราะทุกครั้งที่ขยับตำแหน่ง j คอมพิวเตอร์ต้องไล่บวกเลขใหม่ตั้งแต่ i ถึง j เสมอ หากข้อมูลมีหนึ่งแสนตัว โค้ดนี้อาจต้องใช้เวลาประมวลผลนานจนคุณอาจเกษียณอายุก่อนที่มันจะรันเสร็จครับ

การค้นพบความลับ: มองปัญหาแบบก้าวกระโดด

จุดเปลี่ยนสำคัญไม่ใช่การเรียนรู้อัลกอริทึมใหม่ แต่คือการเปลี่ยนวิธีตั้งคำถามกับโจทย์ แทนที่จะถามว่า "ฉันต้องคำนวณอะไรบ้างในทุกๆ ช่วง?" ให้ลองเปลี่ยนมาถามว่า "ฉันต้องเก็บข้อมูลอะไรไว้บ้างขณะที่กวาดผ่านข้อมูลไปทีละตัว?" นี่คือหัวใจสำคัญของการคิดแบบ Optimal Solution

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

แนวคิดนี้เรียกว่า Kadane's Algorithm ซึ่งจะเปลี่ยนการทำงานจากการวนลูปซ้อนกัน ให้เหลือเพียงการผ่านข้อมูลแค่รอบเดียว (One-pass) โดยเราจะเก็บสถานะไว้สองค่า คือค่าผลรวมที่ดีที่สุดที่จบลงตรงตำแหน่งปัจจุบัน และค่าผลรวมที่ดีที่สุดที่เคยพบมาตลอดการเดินทาง

ลงมือทำ: พลิกโฉมโค้ดให้เป็นระดับ Jedi

เมื่อเราเปลี่ยนวิธีคิด โค้ดจะสั้นลงและทำงานได้เร็วขึ้นอย่างมหาศาล จากเดิมที่ต้องใช้เวลา O(n³) เราจะเหลือเพียง O(n) ซึ่งหมายความว่าถ้าข้อมูลเพิ่มขึ้น 10 เท่า โค้ดของคุณก็จะใช้เวลาเพิ่มขึ้นแค่ 10 เท่า ไม่ใช่ 1,000 เท่าเหมือนวิธีเดิม

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

def max_subarray_kadane(arr):
    best = float('-inf') # เก็บค่าผลรวมที่ดีที่สุดที่เคยเจอ
    current = 0          # เก็บค่าผลรวมที่จบที่ตำแหน่งปัจจุบัน
    for x in arr:
        # ตัดสินใจว่าจะเริ่มใหม่ที่ x หรือบวกต่อจากเดิม
        current = max(x, current + x)
        # อัปเดตค่าที่ดีที่สุด
        best = max(best, current)
    return best

ในโค้ดนี้ current = max(x, current + x) คือหัวใจของการตัดสินใจ หากค่าปัจจุบันบวกเลขใหม่แล้วน้อยกว่าตัวเลขใหม่เอง แปลว่าการเริ่มต้นใหม่คุ้มค่ากว่า นี่คือความฉลาดที่ช่วยให้เราก้าวข้ามขีดจำกัดของโปรแกรมเมอร์มือใหม่ไปสู่ระดับที่สูงขึ้น

กับดักที่ต้องระวัง: บทเรียนจากสนามจริง

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

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

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

สรุป: สู่การเป็นโปรแกรมเมอร์ที่คิดเป็นระบบ

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

เมื่อคุณเจอโจทย์ยากๆ ในอนาคต ไม่ว่าจะเป็นการจัดการฐานข้อมูล การทำ API (ช่องทางให้โปรแกรมคุยกัน) หรือการแก้บั๊กในโปรเจกต์ใหญ่ ให้ลองหยุดคิดสักนิดว่า "เรากำลังทำงานซ้ำซ้อนอยู่ไหม?" และ "มีข้อมูลอะไรบ้างที่ถ้าเราเก็บไว้ จะช่วยให้งานง่ายขึ้น?"

จงจำไว้ว่า โปรแกรมเมอร์ที่เก่งที่สุด ไม่ใช่คนที่เขียนโค้ดได้ซับซ้อนที่สุด แต่เป็นคนที่หาวิธีแก้ปัญหาที่เรียบง่ายและทรงพลังที่สุดได้ต่างหาก ขอให้สนุกกับการเขียนโค้ดและพัฒนาตัวเองในทุกๆ วันครับ!


ที่มา: From brute force to optimal: leveling up like a Jedi — DEV Community

แชร์บทความ

Facebook X LINE

บทความที่เกี่ยวข้อง

จัดการเซิร์ฟเวอร์ผ่าน VS Code ให้ง่ายขึ้นด้วย Easy SSH พร้อมฟีเจอร์โหลดไฟล์ผ่านคลิกเดียว

จัดการเซิร์ฟเวอร์ผ่าน VS Code ให้ง่ายขึ้นด้วย Easy SSH พร้อมฟีเจอร์โหลดไฟล์ผ่านคลิกเดียว

เบื่อไหมที่ต้องสลับหน้าจอไปมาเพื่อจัดการเซิร์ฟเวอร์? มาลองใช้ Easy SSH ปลั๊กอิน VS Code ที่ช่วยให้คุณรีโมทผ่าน Terminal ได้สะดวก แถมโหลดไฟล์ได้ง่ายแค่กด Ctrl+click

ที่มา: DEV Community

3 hours ago 11 นาที
3 views
วิธีดึงข้อมูลราคาจาก Google Hotels ด้วย API สำหรับนักพัฒนา

วิธีดึงข้อมูลราคาจาก Google Hotels ด้วย API สำหรับนักพัฒนา

อยากทำแอปท่องเที่ยวแต่ดึงข้อมูลราคาจาก Google Hotels ไม่ได้? มาดูวิธีใช้ Apify Actor ช่วยดึงข้อมูลแบบอัตโนมัติด้วย Python ง่ายๆ ไม่ต้องกลัวเว็บพัง

ที่มา: DEV Community

6 hours ago 8 นาที
4 views
วิธีเช็กความพร้อมโปรเจกต์ก่อนปล่อยงานจริงด้วย ReleaseReady

วิธีเช็กความพร้อมโปรเจกต์ก่อนปล่อยงานจริงด้วย ReleaseReady

เคยไหม? โค้ดรันได้ในเครื่องแต่พอปล่อยจริงกลับพัง! มาดูวิธีตรวจสอบความพร้อมของโปรเจกต์ก่อนอัปขึ้น GitHub ด้วยเครื่องมือ ReleaseReady กัน

ที่มา: DEV Community

10 hours ago 9 นาที
5 views