Lập trình động không phải là ma thuật
- Lập trình động là một kỹ thuật dùng để giải các bài toán phức tạp bằng cách chia chúng thành các bài toán nhỏ hơn để tiếp cận.
- Kỹ thuật này là một điều tự nhiên, và nhiều thuật toán phổ biến thực chất là áp dụng lập trình động cho những bài toán cụ thể.
- Để giúp việc hiểu về lập trình động dễ hơn, bài viết cung cấp phần giới thiệu đơn giản hơn cùng lời giải thích chi tiết.
Lời than phiền
- Kỹ nghệ phần mềm thường rất tệ trong việc đặt tên.
- Các thuật ngữ như 'bootstrap', 'daemon', 'Cascading Style Sheets', 'cookie', 'trí tuệ nhân tạo' đều mơ hồ hoặc dễ gây hiểu lầm.
- Thuật ngữ 'lập trình động' cũng vậy: bản thân nó không liên quan đến 'động', mà thực ra là một ý tưởng được dùng trong thiết kế thuật toán.
Bộ nhớ đệm cơ bản
- Khi cố gắng giải một bài toán bằng cách chia nó thành các bài toán nhỏ tương tự nhau, bạn có thể phải giải đi giải lại cùng một bài toán nhỏ nhiều lần.
- Lấy dãy Fibonacci làm ví dụ: nếu cài đặt bằng hàm đệ quy đơn giản, bạn sẽ lặp lại cùng một phép tính nhiều lần.
- Có thể tránh tính toán trùng lặp bằng cách lưu kết quả vào bộ nhớ đệm (hoặc memoization).
Bộ nhớ đệm được tối ưu hóa
- Dùng memoization cho phép giữ nguyên đệ quy, nhưng chỉ tính những gì cần khi cần.
- Thay vào đó, cũng có thể tiếp cận bằng cách tính trước mọi thứ cần thiết.
- Cách làm này giải bài toán mà không cần lời gọi đệ quy, và đó chính là lập trình động.
Khoảng cách chỉnh sửa
- Khoảng cách chỉnh sửa giữa hai chuỗi là số lần chỉnh sửa tối thiểu cần để biến một chuỗi thành chuỗi kia.
- Khoảng cách chỉnh sửa có thể được tính bằng các thao tác thay ký tự, chèn và xóa, và đây là bài toán có thể được giải hiệu quả bằng lập trình động.
- Cần tìm ra cách suy ra lời giải tổng thể từ lời giải của các bài toán nhỏ.
Advent of Code, Day 12
- Trong bài toán Advent of Code ngày 12/12/2023, người giải phải xử lý một nonogram 1D.
- Có thể giải bằng cách vét cạn, nhưng độ phức tạp là hàm mũ.
- Thay vào đó, có thể áp dụng lập trình động để giải hiệu quả.
Kết luận
- Lập trình động không dễ, nhưng cũng không phải điều vượt ngoài tầm với của phần lớn lập trình viên.
- Nếu hiểu cách chia bài toán thành các bài toán nhỏ, bạn có thể dùng memoization trong nhiều tình huống, và điều đó mang lại cải thiện lớn so với cách cài đặt ngây thơ.
- Khi làm chủ lập trình động, bạn sẽ hiểu được cả một lớp thuật toán, nắm rõ hơn các đánh đổi và mở ra những tối ưu hóa khác.
Ý kiến của GN⁺
- Lập trình động là một kỹ thuật quan trọng để giải quyết hiệu quả các bài toán phức tạp, giúp giảm đáng kể chi phí tính toán bằng cách lưu lời giải của các bài toán con thay vì lặp lại phép tính trong cách tiếp cận đệ quy.
- Bài viết này rất hữu ích cho các kỹ sư phần mềm mới bắt đầu muốn hiểu các khái niệm cơ bản của lập trình động.
- Các ví dụ về dãy Fibonacci và khoảng cách chỉnh sửa giúp hiểu khái niệm lập trình động, đồng thời mang lại điểm khởi đầu tốt để áp dụng vào giải quyết bài toán thực tế.
1 bình luận
Ý kiến trên Hacker News
Việc bài viết giải thích thuật toán quy hoạch động (DP) như là cache cho đệ quy là một điểm hay.
Thích cách bài viết trước tiên xử lý bài toán theo hướng đệ quy, rồi dần thêm cache và sau đó thu gọn xuống đúng kích thước cache cần thiết.
Một trong những ứng dụng hay của quy hoạch động là căn chỉnh cặp chuỗi axit nucleic/protein.
Chia sẻ trải nghiệm về một giáo sư thuật toán cực kỳ xuất sắc.
Chia sẻ liên kết tới bài viết Quy hoạch động không phải là black magic.
Một người chủ yếu tự học lập trình nói rằng nếu lúc tìm việc mà trong phỏng vấn bị yêu cầu dùng quy hoạch động thì có lẽ họ đã không biết.
Cái tên "quy hoạch động" có thể không khiến người ta nghĩ nó xuất phát từ lĩnh vực lập trình.
Đặt câu hỏi liệu việc chỉ nghĩ đến "memoization" khi nghe "quy hoạch động" có phải là sai hay không.
Quy hoạch động không phải là black magic; điều khó là chứng minh rằng một bài toán có thể được lập trình động và chứng minh tính đúng đắn của nó.
Để thành thạo quy hoạch động, có người khuyên dùng sách thuật toán DPV và khóa học Udacity của Georgia Tech.