Đây là bản xem thử. Phiên bản đầy đủ trong ứng dụng có bài tập, AI chấm ngay và theo dõi tiến độ học.

Bài học

Điều kiện phức hợp và rút gọn logic rẽ nhánh

Mục tiêu bài học

Sau bài này, em kết hợp được `and`/`or`/`not` thành một điều kiện nhiều tầng đúng ngay từ lần viết đầu tiên, thay vì viết if lồng chồng chéo rồi sửa đi sửa lại. Em cũng biết sắp xếp thứ tự các điều kiện con — kiểm tra cái rẻ trước, cái tốn kém trước — để chương trình vừa dễ đọc vừa chạy nhanh hơn.
Cần nhớ trước
if/elif/else, các toán tử so sánh, `and`/`or`/`not`, và cách viết một hàm bằng `def`/`return` — mức đã học ở lớp 10 chuẩn.
Đề chuẩn lớp 10 thường cho một điều kiện tách bạch, ví dụ "kiểm tra n có dương không". Đề HSG thì khác: các điều kiện đan xen nhau, dữ liệu vào không theo thứ tự đã định sẵn (ba cạnh tam giác nhập lộn xộn, không biết cạnh nào lớn nhất), và người ra đề còn quan tâm chương trình có kiểm tra thừa hay không. Ba khác biệt này là chủ đề của bài học.

Bài toán mở đầu — phân loại một tam giác

Cho ba số $a$, $b$, $c$. Viết một hàm trả lời: ba số này có tạo thành một tam giác hợp lệ không, và nếu có thì là tam giác gì — thường, cân, đều, hay vuông? Đề bài **không** nói trước cạnh nào lớn nhất, cũng không đảm bảo ba số luôn tạo được tam giác.

Nếu viết ngay if a == b and b == c: ... rồi if a == b or b == c: ..., em sẽ phải nhân ba số trường hợp thứ tự nhập (a lớn nhất, hoặc b lớn nhất, hoặc c lớn nhất) cho riêng điều kiện tam giác vuông — dễ viết sót, khó kiểm thử hết.

Ý tưởng cốt lõi — chuẩn hóa dữ liệu trước khi rẽ nhánh

Thay vì so sánh từng cặp một cách rời rạc, ta **sắp xếp ba cạnh tăng dần** bằng `sorted()` trước khi kiểm tra bất cứ điều gì. Sau bước này, cạnh lớn nhất luôn nằm ở vị trí cuối — điều kiện tam giác vuông chỉ còn đúng một dạng để viết, không phải ba dạng theo ba cách nhập. Đây là kỹ thuật **chuẩn hóa dữ liệu trước khi rẽ nhánh**: biến nhiều trường hợp thành một trường hợp duy nhất, rồi mới viết logic cho trường hợp đó.
Ví dụ
Ví dụ 1 — Hàm phân loại tam giác đầy đủ
  1. 1
    Đề chuẩn chỉ hỏi ba cạnh có tạo thành tam giác không, và thường cho sẵn thứ tự $a \le b \le c$. Ở đây phải phân biệt bốn nhãn — thường, cân, đều, vuông — cộng thêm nhãn không hợp lệ, với dữ liệu vào không theo thứ tự nào cả.
  2. 2
    Sắp xếp $x \le y \le z$ bằng `sorted()`. Kiểm tra bất đẳng thức tam giác $x + y > z$ **trước tiên** — điều kiện này rẻ và chặn được ngay những bộ số không hợp lệ, tránh phải tính tiếp phần so sánh độ dài hay tổng bình phương cho một bộ số vô nghĩa.
  3. 3
    ```python def classify_triangle(a, b, c): x, y, z = sorted([a, b, c]) if x <= 0 or x + y <= z: return "khong hop le" is_right = abs(x * x + y * y - z * z) < 1e-9 if x == y == z: base = "deu" elif x == y or y == z: base = "can" else: base = "thuong" return base + (", vuong" if is_right else "") ```
  4. 4
    Chạy thử bốn bộ, kể cả một bộ nhập lộn xộn thứ tự và hai bộ biên: `classify_triangle(3, 4, 5)` → `"thuong, vuong"`; `classify_triangle(5, 3, 4)` (đổi thứ tự) → vẫn `"thuong, vuong"`; `classify_triangle(5, 5, 5)` → `"deu"`; `classify_triangle(1, 2, 10)` → `"khong hop le"` (chặn sớm bởi $1 + 2 \le 10$). Kết quả không đổi dù đổi thứ tự tham số — đúng như đề yêu cầu.
  5. 5
    Ta có: gọi $x \le y \le z$ là ba cạnh sau khi sắp xếp tăng dần. Theo bất đẳng thức tam giác, ba đoạn thẳng tạo thành tam giác khi và chỉ khi $x > 0$ và $x + y > z$ (cạnh lớn nhất phải nhỏ hơn tổng hai cạnh còn lại — chỉ cần kiểm tra với cạnh lớn nhất vì đây là điều kiện chặt nhất). Với bộ hợp lệ, tam giác vuông khi và chỉ khi $x^2 + y^2 = z^2$ (định lý Pythagore đảo), tam giác đều khi $x = y = z$, cân khi đúng hai trong ba cạnh bằng nhau. Vậy hàm `classify_triangle` trả về đúng nhãn cho mọi bộ ba số, không phụ thuộc thứ tự nhập.
Kỹ thuật chuẩn hóa dữ liệu giải quyết vế "nhập không theo thứ tự". Vế còn lại — nhiều điều kiện đan xen bằng `and`/`or`/`not` — cần một công cụ khác: rút gọn bằng bảng chân trị.

Rút gọn điều kiện bằng bảng chân trị

Ví dụ
Ví dụ 2 — Rút gọn điều kiện phủ định bằng luật De Morgan
  1. 1

    Một kỳ thi chỉ nhận thí sinh từ 6 đến 18 tuổi. Một bạn viết điều kiện hợp lệ bằng cách phủ định điều kiện KHÔNG hợp lệ: not (age < 6 or age > 18). Cách viết này đúng nhưng khó đọc — hãy rút gọn nó.

  2. 2
    Dùng luật De Morgan: $\text{not}(A \text{ or } B) = (\text{not } A) \text{ and } (\text{not } B)$. Với $A$ là "age < 6" và $B$ là "age > 18", ta có $\text{not } A$ là "age ≥ 6" và $\text{not } B$ là "age ≤ 18". Ghép lại bằng `and` ta được điều kiện trực tiếp, không còn `not` bọc ngoài.
  3. 3
    def age_valid(age):
        return 6 <= age <= 18
    
    
    # kiem chung hai cach viet luon cho cung mot ket qua
    for age in [0, 5, 6, 12, 18, 19, 100]:
        direct = not (age < 6 or age > 18)
        simplified = age_valid(age)
        assert direct == simplified
    
  4. 4
    Ta có $\text{not}(A \text{ or } B) = (\text{not } A) \text{ and } (\text{not } B)$ theo luật De Morgan. Thay $A$: "age < 6", $B$: "age > 18", suy ra điều kiện hợp lệ tương đương `6 <= age <= 18`. Kiểm thử với bảy giá trị biên (0, 5, 6, 12, 18, 19, 100) cho kết quả giống hệt cách viết `not(...)` ban đầu. Vậy `age_valid(age)` là dạng rút gọn đúng và dễ đọc hơn của điều kiện gốc.
Bảng chân trị cũng nhắc em một lỗi rất phổ biến: nhầm $\text{not}(A \text{ or } B)$ thành $(\text{not } A) \text{ or } (\text{not } B)$ — sai một chữ `and`/`or` là đổi hẳn ý nghĩa điều kiện. Luôn kiểm thử lại bằng vài giá trị cụ thể sau khi rút gọn, như Ví dụ 2 vừa làm.

Chặn sớm — giảm phép kiểm tra thừa

Ví dụ
Ví dụ 3 — Sắp thứ tự điều kiện để chặn sớm
  1. 1
    Đề chuẩn chỉ hỏi một điều kiện. Ở đây có bốn điều kiện phải đúng đồng thời: ba số dương, chu vi không vượt quá $P$, tạo thành tam giác hợp lệ, và tam giác đó vuông — thứ tự kiểm tra ảnh hưởng tới số phép tính thực hiện, nhất là khi hàm này còn được gọi hàng nghìn lần trong một vòng lặp ở bài sau.
  2. 2
    Nhờ toán tử `and` có tính "đoản mạch" (short-circuit): Python dừng kiểm tra ngay khi gặp điều kiện `False` đầu tiên, không tính tiếp các điều kiện phía sau. Vậy ta đặt điều kiện **rẻ và dễ sai** lên trước (dương, chu vi), rồi mới tới bất đẳng thức tam giác, và để phép so sánh tổng bình phương — điều kiện "khái niệm" nhất — xuống cuối cùng.
  3. 3
    def is_valid_right_triangle(a, b, c, P):
        if not (a > 0 and b > 0 and c > 0):
            return False
        if a + b + c > P:
            return False
        x, y, z = sorted([a, b, c])
        if x + y <= z:
            return False
        return x * x + y * y == z * z
    
  4. 4
    `is_valid_right_triangle(3, 4, 5, 20)` → `True` (đủ cả bốn điều kiện). `is_valid_right_triangle(3, 4, 5, 11)` → `False`, dừng ngay ở phép kiểm tra chu vi $3+4+5=12 > 11$ — không cần tính tới bất đẳng thức tam giác hay tổng bình phương. `is_valid_right_triangle(-3, 4, 5, 20)` → `False`, dừng ở phép kiểm tra đầu tiên vì $a$ âm.
  5. 5
    Ta có bốn điều kiện nối bằng `and`: dương, chu vi hợp lệ, bất đẳng thức tam giác, và tổng bình phương. Do `and` đoản mạch, thứ tự đặt điều kiện không ảnh hưởng tới **kết quả đúng/sai** nhưng ảnh hưởng tới **số phép tính** khi điều kiện đầu đã sai. Đặt điều kiện rẻ và dễ sai lên trước giúp phần lớn lời gọi hàm dừng sớm. Vậy khi hàm này được gọi trong một vòng lặp lớn quét qua nhiều bộ ba số — như ở các bài brute force sau — thứ tự này tiết kiệm đáng kể thời gian chạy.
Bẫy thường gặp
Ba lỗi hay gặp: (1) so sánh từng cặp cạnh riêng lẻ thay vì sắp xếp trước, dễ bỏ sót trường hợp cạnh lớn nhất nằm ở vị trí đầu; (2) áp dụng De Morgan sai chiều — biến `not(A or B)` thành `(not A) or (not B)` thay vì `and`; (3) đặt điều kiện "tốn" lên trước trong một hàm sẽ được gọi rất nhiều lần, làm chương trình chậm hơn cần thiết dù kết quả vẫn đúng.
Sơ đồ bốn bước xử lý điều kiện phức hợp: nhận diện, chuẩn hóa dữ liệu, sắp thứ tự chặn sớm, rồi kết luận.1Nhận diệnBài có nhiều điềukiện đan xen, dữ liệuvào không theo thứ tựcố định.2Chuẩn hóaSắp xếp hoặc quy vềmột dạng chuẩn trướckhi so sánh (ví dụsorted() ba cạnh).3Sắp thứ tự chặnsớmĐặt điều kiện rẻ, dễsai lên trước nhờ andđoản mạch.4Kết luậnChỉ còn đúng một dạnglogic để viết chotrường hợp đã chuẩnhóa.
Áp dụng cho cả ba ví dụ trên — chuẩn hóa (Ví dụ 1), rút gọn logic (Ví dụ 2), và chặn sớm (Ví dụ 3).
Phân loại các điều kiện của Ví dụ 3 theo mức độ nên kiểm tra trước hay sau.Kiểm tra rẻ —trướca, b, c dươngchu vi ≤ Phai cạnh bằngnhauKiểm tra tốnhơn — sauBĐT tam giácx²+y²=z²(vuông)
Sắp điều kiện rẻ lên trước tận dụng and đoản mạch để chặn sớm những bộ số không hợp lệ.

Luyện tập có hướng dẫn

Luyện tập độc lập

Thử thách

Tóm tắt

Bài này cho em hai công cụ: chuẩn hóa dữ liệu (sắp xếp trước khi so sánh) để gộp nhiều trường hợp nhập thành một, và sắp thứ tự điều kiện theo chi phí (điều kiện rẻ trước, tận dụng `and`/`or` đoản mạch) để vừa dễ đọc vừa chạy nhanh. Luật De Morgan giúp rút gọn một điều kiện phủ định phức tạp thành dạng trực tiếp, dễ kiểm tra lại bằng vài giá trị cụ thể.

Bài tiếp theo dùng lại đúng tinh thần "duyệt có hệ thống" này, nhưng chuyển từ một điều kiện rẽ nhánh sang hai vòng lặp lồng nhau — nơi số phép tính tăng theo bình phương kích thước dữ liệu, và em phải bắt đầu quan tâm khi nào con số đó còn chấp nhận được.