Tài liệu này là một ghi chép Layer 1 phân tích nguyên lý Khoa học Máy tính cốt lõi về thuật toán tăng trưởng lũy thừa của Mảng động (Dynamic Arrays như C++ std::vector, Java ArrayList, Python list, Rust Vec, Go slice), giải thích lý do tại sao việc nhân sức chứa giúp đưa độ phức tạp của thao tác chèn cuối về mức khấu trắc Amortized .
TL;DR
Nếu mảng động tăng sức chứa theo kiểu tuyến tính ( phần tử mỗi lần cấp phát), việc thêm phần tử sẽ yêu cầu lượt copy bộ nhớ với độ phức tạp . Bằng cách áp dụng Thuật toán Tăng trưởng Lũy thừa (Exponential Growth Algorithm) với hệ số nhân (từ đến ), tổng số lần xin RAM giảm xuống lần và đưa chi phí trung bình của mỗi thao tác append về Amortized .
Core Concept & Rationales
1. Thảm Họa Tăng Trưởng Tuyến Tính ()
Trong một mảng động (Dynamic Array), khi mảng hiện tại bị đầy, trình quản lý bộ nhớ không thể mở rộng trực tiếp ô nhớ cũ nếu các ô nhớ tiếp theo trên RAM đã bị đối tượng khác chiếm giữ. Trình quản lý bộ nhớ buộc phải:
- Cấp phát một vùng nhớ mới lớn hơn trên Heap.
- Copy toàn bộ dữ liệu từ mảng cũ sang mảng mới.
- Hủy bỏ mảng cũ.
Nếu mỗi lần tràn capacity, hệ thống chỉ tăng thêm 1 phần tử ():
- Phần tử 1: Copy 0 phần tử.
- Phần tử 2: Copy 1 phần tử.
- …
- Phần tử : Copy phần tử.
Tổng số lượt copy dữ liệu dưới RAM là:
Với , hệ thống phải thực hiện gần 500 tỷ lượt copy bộ nhớ, khiến ứng dụng bị đóng băng hoàn toàn.
2. Nguyên Lý Khấu Trắc Amortized Qua Tăng Trưởng Lũy Thừa
Bằng cách nhân sức chứa theo tỷ lệ lũy thừa (ví dụ hệ số nhân hoặc ):
- Mảng không tăng từng đơn vị mà nhân đôi sức chứa: .
- Số lần phải xin RAM trên Heap giảm từ lần xuống chỉ còn lần.
Phân tích chi phí khấu trắc (Amortized Cost):
Tổng chi phí copy bộ nhớ để chèn phần tử là:
Chia trung bình chi phí cho phần tử:
Thao tác append đạt hiệu năng cực cao ở mức trung bình Amortized .
Practical Implementation
Dưới đây là so sánh hệ số tăng trưởng (Growth Factor) của mảng động trên các ngôn ngữ lập trình phổ biến:
| Ngôn ngữ / Library | Cấu trúc dữ liệu | Growth Factor () | Ghi chú thiết kế |
|---|---|---|---|
| C++ Standard Library | std::vector | (GCC) / (MSVC) | MSVC dùng để tái sử dụng lại các vùng nhớ cũ trên Heap. |
| Java | java.util.ArrayList | (oldCap + (oldCap >> 1)) | Tối ưu cân bằng giữa RAM và tốc độ copy. |
| Python | PyListObject | (newsize + (newsize >> 3) + 6) | Tăng trưởng mịn để tiết kiệm RAM trên hệ thống nhúng/scripting. |
| Rust | std::vec::Vec | Ưu tiên tốc độ tối đa, nhân đôi capacity mỗi khi tràn. | |
| Go | runtime.slice | (cap < 256) | Chuyển tiếp mượt (Smooth Transition) loại bỏ điểm gẫy đột ngột. |
Related Notes
- Stack_vs_Heap_Memory_Fundamentals - Phân tầng bộ nhớ Stack và Heap trong khoa học máy tính.
- Heap_Memory_Size_Classes_and_Alignment - Cơ chế phân chia ô nhớ tiêu chuẩn để tránh phân mảnh RAM.
- Go_Slice_Underlying_Mechanics - Ứng dụng thuật toán tăng trưởng mượt trên Slice của Go.