Chọn đến phần học sinh cần nhanh chóng thông qua mục lục bằng cách click đến phần đó
- I. Lý thuyết trọng tâm
- 1. Đánh giá thời gian thực hiện chương trình
- 2. Phân tích độ phức tạp thời gian thuật toán
- 3. Các quy tắc thực hành tính độ phức tạp
- II. Phương pháp giải các dạng bài tập
- Dạng 1: Xác định độ phức tạp cho chương trình có một vòng lặp
- Dạng 2: Tính O-lớn cho hàm thời gian
- Dạng 3: Xác định độ phức tạp cho thuật toán đã học
- Dạng 4: Đo thời gian thực tế và so sánh
- III. Sơ đồ tư duy
I. Lý thuyết trọng tâm
1. Đánh giá thời gian thực hiện chương trình
Thay vì phải cài đặt và chạy chương trình trên máy tính, ta có thể ước lượng thời gian chạy bằng cách đếm tổng số phép tính đơn và lệnh đơn trong chương trình. Cách tính này tuy không chính xác tuyệt đối so với thời gian thực, nhưng đủ để so sánh các thuật toán với nhau một cách đáng tin cậy.
1.1. Nguyên tắc tính thời gian
Khi đánh giá thời gian, ta quy ước mọi phép toán đơn và lệnh đơn đều tốn 1 đơn vị thời gian như nhau. Cụ thể:
| Loại phép toán / lệnh | Thời gian | Ví dụ |
|---|---|---|
| Phép tính số học (+, −, ×, /, //, %) | 1 đơn vị | a + b, n % 2 |
| Phép so sánh | 1 đơn vị | a > b, x == 0 |
| Phép toán logic (AND, OR, NOT) | 1 đơn vị | a > 0 and b > 0 |
| Lệnh đơn (gán, in, đọc dữ liệu) | 1 đơn vị | x = 5, print(x) |
Vòng lặp for / while |
Tổng thời gian tất cả các bước lặp | Tuỳ số lần lặp |
Lệnh if nhiều nhánh |
Thời gian của nhánh lớn nhất | Xét trường hợp xấu nhất |
1.2. Ví dụ minh hoạ: Hai chương trình
Xét hai chương trình sau:
Chương trình 1 (một vòng lặp):
n = 100 # 1 đơn vị
C = 0 # 1 đơn vị
for k in range(n): # lặp n lần
C = C + 1 # 1 đơn vị mỗi lần → tổng n đơn vị
print(C) # 1 đơn vị
Tổng thời gian: T₁(n) = 1 + 1 + n + 1 = n + 3 đơn vị.
Chương trình 2 (hai vòng lặp lồng nhau):
n = 100 # 1 đơn vị
C = 0 # 1 đơn vị
for i in range(n): # lặp n lần
for j in range(n): # mỗi lần lặp tiếp n lần → tổng n × n = n² lần
C = C + 1 # 1 đơn vị mỗi lần → tổng n² đơn vị
print(C) # 1 đơn vị
Tổng thời gian: T₂(n) = 1 + 1 + n² + 1 = n² + 3 đơn vị.

So sánh: Với n = 100, chương trình 1 cần khoảng 103 đơn vị, trong khi chương trình 2 cần khoảng 10.003 đơn vị – chênh lệch gần 100 lần. Khi n càng lớn, khoảng cách càng tăng mạnh.
1.3. Phép toán tích cực
Trong một chương trình, phép toán được thực hiện nhiều lần nhất và đóng vai trò quyết định đến thời gian chạy được gọi là phép toán tích cực.
- Trong chương trình 1: phép toán tích cực là
C = C + 1tại dòng 4 (thực hiện n lần). - Trong chương trình 2: phép toán tích cực cũng là
C = C + 1tại dòng 5 (thực hiện n² lần).
Xác định được phép toán tích cực giúp ta nhanh chóng ước lượng thời gian chạy mà không cần đếm chi tiết từng lệnh.
2. Phân tích độ phức tạp thời gian thuật toán
2.1. Khái niệm
Độ phức tạp thời gian thuật toán là khối lượng thời gian cần thiết để thực hiện thuật toán, biểu diễn dưới dạng hàm T(n), trong đó n là kích thước dữ liệu đầu vào. Khi n tăng lên, T(n) cũng tăng nhưng với tốc độ tăng khác nhau tuỳ thuộc vào thuật toán.
Để phân loại và so sánh tốc độ tăng của các hàm thời gian, các nhà khoa học sử dụng kí hiệu O-lớn (big-O notation).
2.2. Định nghĩa kí hiệu O-lớn
Cho f(n) và g(n) là hai hàm có đối số là số tự nhiên. Ta viết f(n) = O(g(n)) và nói “f(n) có bậc O-lớn của g(n)” nếu tồn tại hằng số c > 0 và số tự nhiên n₀ ≥ 1 sao cho với mọi n ≥ n₀:
f(n) ≤ c · g(n)
Nói đơn giản: khi n đủ lớn, f(n) không tăng nhanh hơn một bội số nào đó của g(n).
2.3. Ví dụ tính O-lớn
Ví dụ 1: Chương trình 1 có T₁(n) = n + 3.
Chọn c = 2, n₀ = 3. Khi n ≥ 3: T₁(n) = n + 3 ≤ n + n = 2n = c · n.
Vậy T₁(n) = O(n) – độ phức tạp tuyến tính.
Ví dụ 2: Chương trình 2 có T₂(n) = n² + 3.
Chọn c = 2, n₀ = 2. Khi n ≥ 2: T₂(n) = n² + 3 < n² + n₀² ≤ n² + n² = 2n² = c · n².
Vậy T₂(n) = O(n²) – độ phức tạp bình phương.
2.4. Các bậc độ phức tạp thường gặp
Các thuật toán được phân loại theo các bậc độ phức tạp từ nhanh đến chậm:
| Kí hiệu | Tên gọi | Ý nghĩa | Ví dụ |
|---|---|---|---|
| O(1) | Hằng số | Thời gian không phụ thuộc n | Truy cập phần tử mảng theo chỉ số |
| O(log n) | Logarit | Rất nhanh, n tăng gấp đôi thì thời gian chỉ tăng thêm 1 | Tìm kiếm nhị phân |
| O(n) | Tuyến tính | Thời gian tăng tỉ lệ thuận với n | Tìm kiếm tuần tự |
| O(n log n) | Tuyến tính logarit | Nhanh hơn O(n²) | Một số thuật toán sắp xếp nâng cao |
| O(n²) | Bình phương | Thời gian tăng theo bình phương n | Sắp xếp chèn, chọn, nổi bọt |
| O(nᵏ) | Đa thức | Thời gian tăng theo luỹ thừa bậc k | Thuật toán có k vòng lặp lồng nhau |
| O(aⁿ) | Luỹ thừa | Tăng cực nhanh | Một số bài toán tổ hợp |
| O(n!) | Giai thừa | Tăng nhanh nhất | Duyệt mọi hoán vị |
3. Các quy tắc thực hành tính độ phức tạp
Để tính nhanh O-lớn mà không cần dùng định nghĩa chặt chẽ, ta áp dụng hai quy tắc sau:
3.1. Quy tắc cộng
O(f(n) + g(n)) = O(max(f(n), g(n)))
Khi hai đoạn chương trình thực hiện nối tiếp nhau, độ phức tạp tổng bằng độ phức tạp của đoạn lớn hơn.
Ví dụ: Nếu đoạn 1 có T = O(n) và đoạn 2 có T = O(n²), thì tổng là O(max(n, n²)) = O(n²).
3.2. Quy tắc nhân
Nhân với hằng số: O(C · f(n)) = O(f(n)), với C là hằng số bất kì.
Hằng số nhân không ảnh hưởng đến bậc của hàm. Ví dụ: O(10n²) = O(n²).
Nhân hai hàm: O(f(n) · g(n)) = O(f(n)) · O(g(n)).
Quy tắc này áp dụng khi có hai vòng lặp lồng nhau. Ví dụ: vòng ngoài O(n), vòng trong O(n) → tổng là O(n) · O(n) = O(n²).
3.3. Ví dụ áp dụng quy tắc
Ví dụ 1: T(n) = 10n² → O(10n²) = O(n²) (quy tắc nhân với hằng số).
Ví dụ 2: T(n) = 3n² + n·log(n).
- Áp dụng quy tắc cộng: O(max(3n², n·log(n))) = O(3n²).
- Áp dụng quy tắc nhân hằng số: O(3n²) = O(n²).
Ví dụ 3: T(n) = 2n(n − 2) + 4 = 2n² − 4n + 4.
- Số hạng bậc cao nhất là 2n² → O(2n²) = O(n²).
Ví dụ 4: T(n) = n³ + 5n − 3.
- Số hạng bậc cao nhất là n³ → O(n³).
Mẹo nhanh: Khi tính O-lớn cho đa thức, chỉ cần lấy số hạng bậc cao nhất rồi bỏ hệ số. Ví dụ: 5n³ + 2n² + 100n + 999 → O(n³).
II. Phương pháp giải các dạng bài tập
Dạng 1: Xác định độ phức tạp cho chương trình có một vòng lặp
Phương pháp: Đếm số lần lặp của vòng for hoặc while, đó chính là số lần thực hiện phép toán tích cực. Nếu vòng lặp chạy n lần → O(n). Nếu mỗi bước nhân đôi biến → O(log n).
Bài 1 (Luyện tập SGK). Xác định độ phức tạp thời gian cho chương trình sau:
n = 1000
S = 0
for i in range(n):
S = S + i * (i + 1)
print(S)
Lời giải:
Phân tích từng dòng:
- Dòng 1, 2: Lệnh gán → 1 + 1 = 2 đơn vị.
- Dòng 3–4: Vòng
forlặp n lần. Mỗi lần thực hiện phép nhâni * (i + 1)(1 đơn vị) và phép cộngS = S + ...(1 đơn vị) → mỗi bước lặp cần 2 đơn vị → tổng 2n đơn vị. - Dòng 5: Lệnh
print→ 1 đơn vị.
Tổng: T(n) = 2 + 2n + 1 = 2n + 3.
Phép toán tích cực: S = S + i * (i + 1) (thực hiện n lần).
Áp dụng quy tắc nhân hằng số: O(2n + 3) = O(2n) = O(n) – tuyến tính.
Bài 2 (Luyện tập SGK). Xác định độ phức tạp thời gian cho chương trình sau:
n = 1000
Sum = 0
i = 1
while i < n:
i = i * 2
Sum = Sum + i
print(Sum)
Lời giải:
Phân tích vòng while:
- Ban đầu
i = 1. Sau mỗi bước lặp,iđược nhân đôi: 1 → 2 → 4 → 8 → 16 → … → cho đến khii ≥ n. - Hỏi: bao nhiêu bước để i đạt hoặc vượt n? Sau k bước, i = 2ᵏ. Cần 2ᵏ ≥ n, tức k ≥ log₂(n). Vậy vòng
whilelặp khoảng log₂(n) lần.
Phân tích thời gian:
- Dòng 1, 2, 3: 3 đơn vị.
- Vòng
while: log₂(n) bước lặp, mỗi bước cần 2 đơn vị (nhân và cộng) → tổng 2·log₂(n) đơn vị. - Dòng 7: 1 đơn vị.
Tổng: T(n) = 3 + 2·log₂(n) + 1 = 2·log₂(n) + 4.
Phép toán tích cực: i = i * 2 và Sum = Sum + i (thực hiện log₂(n) lần).
Độ phức tạp: O(2·log₂(n) + 4) = O(log n) – logarit.
Nhận biết nhanh: Khi biến lặp nhân đôi (hoặc chia đôi) mỗi bước → độ phức tạp là O(log n).
Dạng 2: Tính O-lớn cho hàm thời gian
Phương pháp: Xác định số hạng bậc cao nhất trong biểu thức, bỏ hệ số. Hoặc áp dụng quy tắc cộng và nhân.
Bài phụ (câu hỏi trong SGK).
a) T(n) = n³ + n·log(n) + 2n + 1.
Áp dụng quy tắc cộng: O(max(n³, n·log(n), 2n, 1)) = O(n³). Vì n³ tăng nhanh nhất.
→ O(n³).
b) T(n) = 3n⁴ + 2n²·log(n) + 10.
Áp dụng quy tắc cộng: O(max(3n⁴, 2n²·log(n), 10)) = O(3n⁴) = O(n⁴).
→ O(n⁴).
Dạng 3: Xác định độ phức tạp cho thuật toán đã học
Phương pháp: Phân tích cấu trúc vòng lặp trong thuật toán: vòng lặp đơn → O(n), hai vòng lặp lồng nhau mỗi vòng n lần → O(n²).
Bài 3 (Vận dụng SGK). Xác định độ phức tạp thời gian của thuật toán sắp xếp chọn (Selection Sort).
Lời giải:
Xét code thuật toán sắp xếp chọn:
def SelectionSort(A):
n = len(A) # 1 đơn vị
for i in range(n - 1): # vòng ngoài: n-1 lần
iMin = i # 1 đơn vị
for j in range(i + 1, n): # vòng trong: (n-1-i) lần
if A[j] < A[iMin]: # 1 đơn vị (phép so sánh)
iMin = j # 1 đơn vị
A[i], A[iMin] = A[iMin], A[i] # 1 đơn vị
Phân tích vòng lặp trong:
- Khi i = 0: vòng trong chạy n − 1 lần.
- Khi i = 1: vòng trong chạy n − 2 lần.
- …
- Khi i = n − 2: vòng trong chạy 1 lần.
Tổng số lần thực hiện phép so sánh (phép toán tích cực):
(n − 1) + (n − 2) + … + 1 = n(n − 1)/2 = (n² − n)/2
Áp dụng quy tắc: O((n² − n)/2) = O(n²/2) = O(n²) – bình phương.
Vậy thuật toán sắp xếp chọn có độ phức tạp thời gian O(n²).
Ghi nhớ: Cả ba thuật toán sắp xếp đã học (chèn, chọn, nổi bọt) đều có độ phức tạp thời gian O(n²) trong trường hợp xấu nhất.
Dạng 4: Đo thời gian thực tế và so sánh
Bài 4 (Vận dụng SGK). Thiết lập chương trình và tính thời gian chạy thực tế của chương trình 1 và chương trình 2 (Hình 24.2) với các giá trị n khác nhau.
Lời giải:
from time import perf_counter
def chuong_trinh_1(n):
C = 0
for k in range(n):
C = C + 1
return C
def chuong_trinh_2(n):
C = 0
for i in range(n):
for j in range(n):
C = C + 1
return C
# Thử với các giá trị n khác nhau
for n in [100, 1000, 5000, 10000]:
# Đo chương trình 1
t1 = perf_counter()
chuong_trinh_1(n)
t2 = perf_counter()
time1 = t2 - t1
# Đo chương trình 2
t3 = perf_counter()
chuong_trinh_2(n)
t4 = perf_counter()
time2 = t4 - t3
print(f"n = {n:>6} | CT1: {time1:.6f}s | CT2: {time2:.6f}s | CT2/CT1: {time2/time1:.1f} lần")
Giải thích: Chương trình đo thời gian chạy của cả hai thuật toán với nhiều giá trị n. Cột CT2/CT1 cho thấy chương trình 2 chậm hơn chương trình 1 bao nhiêu lần. Khi n tăng gấp đôi, thời gian chương trình 1 (O(n)) tăng khoảng gấp đôi, trong khi thời gian chương trình 2 (O(n²)) tăng khoảng gấp bốn. Đây chính là ý nghĩa thực tế của sự khác biệt giữa O(n) và O(n²).
III. Sơ đồ tư duy
┌─ Phép tính số học: 1 đơn vị
├─ Phép so sánh, logic: 1 đơn vị
┌─ Nguyên tắc tính thời gian ────┼─ Lệnh đơn (gán, in): 1 đơn vị
│ ├─ Vòng lặp: tổng thời gian các bước
│ └─ Lệnh if: thời gian nhánh lớn nhất
│
│ ┌─ Định nghĩa: f(n) ≤ c·g(n) khi n đủ lớn
├─ Kí hiệu O-lớn (big-O) ────────┤
│ └─ Các bậc: O(1) < O(logn) < O(n)
│ < O(nlogn) < O(n²) < O(n³) < O(aⁿ)
│
│ ┌─ QT cộng: O(f + g) = O(max(f, g))
├─ Quy tắc tính nhanh ───────────┤ → hai đoạn nối tiếp: lấy cái lớn hơn
│ └─ QT nhân: O(C·f) = O(f)
│ O(f·g) = O(f)·O(g)
│ → vòng lặp lồng nhau: nhân lại
│
└─ Phép toán tích cực ─── Phép toán thực hiện nhiều nhất trong CT
→ quyết định chính đến thời gian chạy

Thầy Phạm Thành Danh
(Người kiểm duyệt, ra đề)
Chức vụ: Trưởng ban biên soạn môn Tin Học THPT
Trình độ: Cử nhân Sư phạm Tin học, Chứng chỉ hạng II, Chứng chỉ Tin học, Ngoại ngữ B1
Kinh nghiệm: 8+ năm kinh nghiệm tại Trường THPT Thuận Hóa
