- Thi HSG Tin học, Olympic, ICPC: Chọn C++ vì vòng lặp chạy nhanh hơn Python hàng chục lần (bảng đo bên dưới: 11 đến 58 lần), kiểm soát bộ nhớ chặt và có sẵn
std::set,std::maptrong thư viện STL. - Mới bắt đầu từ con số 0 hoặc định hướng AI, Khoa học dữ liệu: Chọn Python vì cú pháp ngắn gọn và hỗ trợ số nguyên lớn tự động (
arbitrary-precision int) không bao giờ tràn số. - Luyện LeetCode và phỏng vấn Big Tech: Cả hai đều tốt. Python giúp viết xong lời giải trên bảng trắng rất nhanh; C++ giúp thể hiện hiểu biết sâu về kiểu dữ liệu và bộ nhớ.
- Tư duy quan trọng hơn cú pháp: Khi đã nắm vững 7 bước tư duy giải quyết bài toán, việc chuyển đổi qua lại giữa Python và C++ chỉ mất khoảng 2 đến 3 tuần.
Đặt C++17 và Python 3 lên cùng một bài toán thuật toán
Để thấy rõ sự khác biệt, hãy đặt C++17 và Python 3 cạnh nhau trên hai tác vụ số học quen thuộc với giới hạn n = 100.000:
- Sàng nguyên tố Eratosthenes: Đếm số lượng số nguyên tố từ
1đếnnvà tính tổng của chúng. - Đếm tổng số ước trên đoạn: Dùng hai vòng lặp nhảy bội để tính tổng số ước nguyên dương
d(1) + d(2) + ... + d(n)trong thời gianO(n log n).
#include <iostream>
#include <vector>
using namespace std;
// 1. Sàng Eratosthenes đếm và tính tổng các số nguyên tố <= n
pair<int, long long> thong_ke_nguyen_to(int n) {
vector<bool> la_nt(n + 1, true);
la_nt[0] = la_nt[1] = false;
for (int p = 2; p * p <= n; ++p) {
if (la_nt[p]) {
for (int boi = p * p; boi <= n; boi += p) {
la_nt[boi] = false;
}
}
}
int so_luong = 0;
long long tong = 0;
for (int i = 2; i <= n; ++i) {
if (la_nt[i]) {
++so_luong;
tong += i;
}
}
return {so_luong, tong};
}
// 2. Tính tổng số ước của mọi số từ 1 đến n trong O(n log n)
long long tong_so_uoc(int n) {
vector<int> so_uoc(n + 1, 0);
for (int i = 1; i <= n; ++i) {
for (int j = i; j <= n; j += i) {
++so_uoc[j];
}
}
long long tong = 0;
for (int i = 1; i <= n; ++i) {
tong += so_uoc[i];
}
return tong;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n = 100000;
auto [so_luong_nt, tong_nt] = thong_ke_nguyen_to(n);
long long tong_uoc = tong_so_uoc(n);
// Kết quả: 9592 454396537 1166750
cout << so_luong_nt << " " << tong_nt << " " << tong_uoc << "\n";
return 0;
}
def thong_ke_nguyen_to(n: int) -> tuple[int, int]:
"""Sàng Eratosthenes đếm và tính tổng các số nguyên tố <= n."""
la_nt = bytearray([1]) * (n + 1)
la_nt[0] = la_nt[1] = 0
p = 2
while p * p <= n:
if la_nt[p]:
# Gán hàng loạt bằng slice trong Python nhanh hơn vòng for thuần
la_nt[p * p : n + 1 : p] = bytearray(len(range(p * p, n + 1, p)))
p += 1
so_luong = sum(la_nt)
tong = sum(i for i, nt in enumerate(la_nt) if nt)
return so_luong, tong
def tong_so_uoc(n: int) -> int:
"""Tính tổng số ước của mọi số từ 1 đến n trong O(n log n)."""
so_uoc = [0] * (n + 1)
for i in range(1, n + 1):
for j in range(i, n + 1, i):
so_uoc[j] += 1
return sum(so_uoc)
if __name__ == "__main__":
n = 100_000
so_luong_nt, tong_nt = thong_ke_nguyen_to(n)
tong_uoc = tong_so_uoc(n)
# Kết quả: 9592 454396537 1166750
print(so_luong_nt, tong_nt, tong_uoc)
Cả hai chương trình đều in ra 9592 454396537 1166750 (từ 1 đến 100.000 có 9.592 số nguyên tố, tổng là 454.396.537 và tổng số ước trên đoạn là 1.166.750). Tuy nhiên, khi tăng kích thước lên n = 10⁶, sự khác biệt về thời gian chạy lộ rõ:
| Bài toán thử nghiệm | Kích thước n |
C++17 (g++ -O2) |
Python 3.14 (vòng lặp thuần) | Python 3.14 (gán slice) | Chênh lệch C++ so với Python |
|---|---|---|---|---|---|
Sàng nguyên tố O(n log log n) |
10⁵ |
~0.34 ms |
~9.2 ms |
~3.8 ms |
Nhanh hơn 11 – 27 lần |
Sàng nguyên tố O(n log log n) |
10⁶ |
~3.5 ms |
~104 ms |
~42 ms |
Nhanh hơn 12 – 30 lần |
Tổng số ước O(n log n) |
10⁵ |
~1.0 ms |
~60 ms |
— | Nhanh hơn ~58 lần |
Tổng số ước O(n log n) |
10⁶ |
~22 ms |
~888 ms |
— | Nhanh hơn ~40 lần |
Số đo trên một máy tính xách tay chạy Windows 11 (g++ 16.1 với -O2, Python 3.14), lấy lần nhanh nhất trong 5 đến 9 lần chạy. Cột "vòng lặp thuần" thay dòng gán slice trong hàm sàng bằng một vòng for. Máy khác sẽ ra số khác, nhưng tỉ lệ chênh lệch gần như vậy.
Ở bài toán tính tổng số ước với n = 10⁶, tổng số bước lặp là 10⁶ × (1/1 + 1/2 + ... + 1/10⁶) ≈ 1,44 × 10⁷. C++17 chạy xong chỉ trong 22 mili-giây, trong khi vòng lặp for thuần túy của Python mất gần 0,9 giây — sát ngưỡng giới hạn 1.0 giây của các hệ thống chấm bài.
Vì sao C++ nhanh hơn hàng chục lần ở vòng lặp thuần túy?
Khoảng cách tốc độ này đến từ cơ chế thực thi bên dưới phần cứng của hai ngôn ngữ:
Có 3 nguyên nhân kỹ thuật cốt lõi:
- Biên dịch ra mã máy (Compiled) so với Thông dịch Bytecode (Interpreted):
Trình biên dịch
g++ -O2dịch toàn bộ mã nguồn C++ trực tiếp thành tập lệnh mã máy của CPU trước khi chạy và tận dụng thanh ghi phần cứng. Ngược lại, CPython dịch mã nguồn thành lệnh trung gian (bytecode) rồi dùng máy ảo đọc và giải mã từng lệnh trong lúc chạy. - Kiểu dữ liệu tĩnh (Static Typing) so với Kiểu động (Dynamic Typing):
Trong C++, biến
int jlà số nguyên 32-bit cố định nên phép cộng++so_uoc[j]dịch ra đúng 1 lệnh CPU. Trong Python, mọi biến đều trỏ tới một đối tượngPyObjecttrên bộ nhớ Heap; ở mỗi bước lặp, máy ảo phải kiểm tra kiểu dữ liệu, đọc giá trị rồi cấp phát đối tượng số nguyên mới. - Tính cục bộ bộ nhớ đệm (CPU Cache Locality):
Mảng
vector<int>gồm10⁶phần tử trong C++ là dải4 MBnằm liên tiếp nhau trên RAM, giúp bộ nhớ đệm L1/L2 Cache của CPU nạp dữ liệu cực nhanh. Trong khi đó, mộtlistcủa Python là mảng các con trỏ trỏ tới các đối tượng nằm rải rác trên Heap.
Ưu thế của Python: Số nguyên lớn tự động và cú pháp cô đọng
Đổi lại tốc độ vòng lặp, Python sở hữu hai lợi thế giúp tiết kiệm nhiều thời gian gỡ lỗi: số nguyên độ chính xác tùy ý (arbitrary-precision int) và cú pháp xử lý mảng ngắn gọn.
Trong C++, kiểu số nguyên chuẩn lớn nhất là long long (64-bit có dấu), chỉ lưu tối đa 2⁶³ - 1 ≈ 9,22 × 10¹⁸. Ngay khi tính 21!, biến long long sẽ bị tràn số (integer overflow) nếu bạn không chia lấy dư ở từng bước hoặc tự cài đặt cấu trúc Số Lớn (BigInt). Ngược lại, kiểu int của Python 3 tự động mở rộng bộ nhớ khi số phình to, tính trực tiếp 30! hay 100! mà không bao giờ tràn số.
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
// 1. Giai thừa dùng long long (tối đa tới 20! trước khi tràn 64-bit có dấu)
long long giai_thua_64bit(int n) {
long long kq = 1;
for (int i = 2; i <= n; ++i) {
kq *= i;
}
return kq;
}
// 2. Khi n > 20, C++ phải lấy dư theo MOD ở từng bước nhân để không tràn số
long long giai_thua_mod(int n, long long mod) {
long long kq = 1;
for (int i = 2; i <= n; ++i) {
kq = (kq * i) % mod;
}
return kq;
}
// 3. Lọc số chẵn, bình phương và lấy 3 giá trị lớn nhất
vector<int> top3_binh_phuong_chan(const vector<int>& a) {
vector<int> bp;
for (int x : a) {
if (x % 2 == 0) {
bp.push_back(x * x);
}
}
sort(bp.begin(), bp.end(), greater<int>());
if (bp.size() > 3) {
bp.resize(3);
}
return bp;
}
int main() {
cout << giai_thua_64bit(20) << "\n";
cout << giai_thua_mod(30, 1000000007LL) << "\n";
vector<int> a = {7, 2, 10, 4, 9, 6, 1, 8};
vector<int> top3 = top3_binh_phuong_chan(a);
for (size_t i = 0; i < top3.size(); ++i) {
cout << top3[i] << (i + 1 == top3.size() ? '\n' : ' ');
}
return 0;
}
import math
def giai_thua_64bit(n: int) -> int:
"""Python tự động mở rộng số nguyên lớn (arbitrary-precision int)."""
return math.factorial(n)
def giai_thua_mod(n: int, mod: int) -> int:
"""Tính n! % mod (có thể chia lấy dư từng bước hoặc lấy math.factorial(n) % mod)."""
kq = 1
for i in range(2, n + 1):
kq = (kq * i) % mod
return kq
def top3_binh_phuong_chan(a: list[int]) -> list[int]:
"""Lọc số chẵn, bình phương và lấy 3 giá trị lớn nhất bằng list comprehension."""
return sorted((x * x for x in a if x % 2 == 0), reverse=True)[:3]
if __name__ == "__main__":
print(giai_thua_64bit(20))
# Python tính trực tiếp 30! = 265252859812191058636308480000000 không tràn số
assert math.factorial(30) % 1_000_000_007 == giai_thua_mod(30, 1_000_000_007)
print(giai_thua_mod(30, 1_000_000_007))
a = [7, 2, 10, 4, 9, 6, 1, 8]
print(*top3_binh_phuong_chan(a))
Cả hai chương trình đều in ra kết quả đối chiếu khớp nhau:
- Dòng 1 (
20!):2432902008176640000 - Dòng 2 (
30! mod 1000000007):109361473 - Dòng 3 (3 bình phương chẵn lớn nhất):
100 64 36
Ở tác vụ lọc các số chẵn, bình phương và lấy 3 số lớn nhất: C++ cần vòng lặp for, push_back, sort và resize; trong khi Python chỉ cần 1 dòng sorted((x * x for x in a if x % 2 == 0), reverse=True)[:3].
Bảng chọn ngôn ngữ theo 4 mục tiêu rõ ràng
Bạn có thể quyết định ngay nên học ngôn ngữ nào trước bằng cách đối chiếu với mục tiêu trong 6 đến 12 tháng tới:
| Mục tiêu của bạn | Ngôn ngữ khuyên chọn | Lý do quyết định |
|---|---|---|
| 1. Thi HSG Tin học (Tỉnh, Quốc gia), Olympic, ICPC | Chọn C++ (C++17 / C++20) | Đề thi yêu cầu tốc độ vòng lặp 10⁸ phép tính/giây và cấu trúc cây tìm kiếm nhị phân cân bằng std::set, std::map có sẵn trong C++ STL. |
| 2. Mới học từ con số 0, hoặc định hướng AI, Data Science, Tự động hóa | Chọn Python 3 | Không vướng con trỏ hay tràn số 64-bit; tập trung 100% vào logic giải thuật và nối thẳng sang hệ sinh thái NumPy, Pandas, PyTorch. |
| 3. Luyện LeetCode và phỏng vấn tại Big Tech | Cả hai đều tốt | Trong 45 phút phỏng vấn, Python giúp viết code nhanh và gọn hơn; còn C++ là điểm cộng lớn nếu ứng tuyển vị trí Backend hiệu năng cao hoặc Hệ thống. |
| 4. Học Cấu trúc dữ liệu & Giải thuật ở Đại học | Học C++ hiểu gốc bộ nhớ, dùng thêm Python kiểm thử | C++ giúp hiểu sâu cấp phát bộ nhớ, tham chiếu và con trỏ; kết hợp Python để viết nhanh trình sinh test kiểm tra chéo. |
Bảng đối chiếu thư viện thuật toán giữa C++ STL và Python
Khi cài đặt các thuật toán như tìm kiếm nhị phân, sắp xếp nhanh Quick Sort hay sắp xếp trộn Merge Sort, bạn có thể tra cứu nhanh các cấu trúc tương đương giữa hai bên:
| Cấu trúc / Thao tác | C++17 (Thư viện STL) | Python 3 (Thư viện chuẩn) | Ghi chú quan trọng |
|---|---|---|---|
| Mảng động | std::vector<int> |
list |
Truy cập phần tử O(1) và thêm cuối O(1) trung bình |
| Sắp xếp mảng | std::sort(a.begin(), a.end()) |
a.sort() hoặc sorted(a) |
C++ dùng Introsort; Python dùng Timsort (ổn định) |
| Tìm kiếm nhị phân | std::lower_bound, std::upper_bound |
bisect.bisect_left, bisect.bisect_right |
Đều chạy trong O(log n) trên mảng đã sắp xếp |
| Hàng đợi 2 đầu (BFS) | std::deque<int> / std::queue<int> |
collections.deque |
Trong Python tránh dùng list.pop(0) vì tốn O(n) |
| Hàng đợi ưu tiên (Heap) | std::priority_queue<int> (Max-Heap) |
heapq (Min-Heap) |
C++ mặc định lấy max trước; Python lấy min trước |
| Bảng băm (Hash Table) | std::unordered_map, std::unordered_set |
dict, set |
Tra cứu trung bình O(1) |
| Cây cân bằng (Ordered Set) | std::set, std::map, std::multiset |
(Không có sẵn trong thư viện chuẩn) | Lợi thế lớn nhất của C++ trong lập trình thi đấu |
| Ước chung & Lũy thừa dư | std::gcd(a, b) |
math.gcd(a, b) và pow(a, b, mod) |
Python tích hợp sẵn lũy thừa nhị phân pow(a, b, m) |
Lộ trình kết hợp cả hai ngôn ngữ và lỗi hay gặp
Bản chất thuật toán nằm ở tư duy phân tích và đánh giá độ phức tạp chứ không phụ thuộc cú pháp:
- Giai đoạn 1 (2–3 tháng đầu): Chọn một ngôn ngữ duy nhất theo bảng mục tiêu để học từ biến, vòng lặp, mảng tới thuật toán Euclid, phép toán bit và tìm kiếm nhị phân.
- Giai đoạn 2 (Sau 80–100 bài): Dành 2 tuần viết lại 20 bài quen thuộc bằng ngôn ngữ còn lại.
- Tràn số
inttrong C++: Biểu thứclong long s = a * a;vớiint a = 100000vẫn nhân ở kiểuint32-bit trước (bị tràn số) rồi mới gán vàos. Hãy viết1LL * a * a. - Dùng
list.pop(0)hoặcx in listtrong vòng lặp Python: Cả hai thao tác đều tốnO(n). Hãy dùngcollections.deque.popleft()cho hàng đợi vàsetđể tra cứuO(1). - Quên tăng tốc nhập xuất: Khi đọc hơn
10⁵dòng, C++ cầnios::sync_with_stdio(false); cin.tie(nullptr);, còn Python nên dùngsys.stdin.readline.
Luyện ngay trên Big-O Coding
Hệ thống Big-O Coding hỗ trợ đầy đủ cả C++ và Python. Hãy thử sức ngay với các bài tập nhập môn dưới đây:
Câu hỏi thường gặp
Chưa biết lập trình mà học thẳng C++ có quá khó không?
Không quá khó nếu bạn đi từng bước từ biến, vòng lặp đến std::vector và std::string. Ở giai đoạn nhập môn thuật toán, bạn chưa cần đụng tới quản lý con trỏ cấp thấp phức tạp.
Đi thi Học sinh giỏi Tin học có được dùng Python không?
Nhiều kỳ thi HSG tỉnh và quốc gia hiện đã cho phép dùng Python. Tuy nhiên, giới hạn thời gian và các bài cần cây cân bằng (std::set, std::map) vẫn ưu ái C++ hơn, nên C++ vẫn là lựa chọn chủ lực.
Luyện LeetCode phỏng vấn bằng Python có bị đánh giá thấp hơn C++ không?
Hoàn toàn không. Các công ty công nghệ lớn cho phép ứng viên tự chọn ngôn ngữ và chấm điểm dựa trên tư duy thuật toán, độ phức tạp thời gian/bộ nhớ cùng cách xử lý trường hợp biên.
Khóa Green cho người mới, sinh viên năm 1–2: xây nền tảng lập trình và thuật toán vững chắc, biết dùng AI hỗ trợ. 16 buổi online, học thử buổi 1 miễn phí.

