Phép chia dư (modulo): thủ thuật nhỏ, ứng dụng rất lớn

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Phép chia lấy dư (modulo, viết là % trong nhiều ngôn ngữ) trông chỉ là một phép toán lẻ loi của lớp tiểu học. Thế nhưng nó xuất hiện khắp nơi: kiểm tra chẵn lẻ, xoay vòng danh sách, tính giờ, mã hóa, bảng băm.

Modulo là gì?

Khi chia a cho b, ta được thương và số dư. Phép modulo trả về số dư:

print(17 % 5)    # 2  vì 17 = 3 x 5 + 2
print(20 % 4)    # 0  vì chia hết
print(3 % 7)     # 3  vì số bị chia nhỏ hơn số chia
print(17 // 5)   # 3  thương nguyên

Kết quả của a % b luôn nằm trong khoảng từ 0 đến b - 1 (với b dương). Chính tính chất "bị chặn trong một khoảng" này làm modulo hữu ích đến vậy.

Ứng dụng 1: kiểm tra chẵn lẻ và chia hết

def la_chan(n):
    return n % 2 == 0

def chia_het(a, b):
    return a % b == 0

print(la_chan(10))        # True
print(chia_het(15, 5))    # True

Bài FizzBuzz nổi tiếng trong các buổi phỏng vấn cũng chỉ dựa trên điều này: in ra Fizz nếu chia hết cho 3, Buzz nếu chia hết cho 5.

for i in range(1, 16):
    if i % 15 == 0:
        print("FizzBuzz")
    elif i % 3 == 0:
        print("Fizz")
    elif i % 5 == 0:
        print("Buzz")
    else:
        print(i)

Ứng dụng 2: xoay vòng (wrap-around)

Giả sử có 5 slide, sau slide cuối quay lại slide đầu. Dùng modulo thay vì viết điều kiện if:

so_slide = 5
hien_tai = 4
ke_tiep = (hien_tai + 1) % so_slide
print(ke_tiep)   # 0, quay về đầu

truoc = (0 - 1) % so_slide
print(truoc)     # 4 trong Python

Lưu ý: với số âm, kết quả của % khác nhau giữa các ngôn ngữ. Python cho kết quả cùng dấu với số chia (nên luôn không âm khi chia cho số dương), còn C, C++, Java, JavaScript có thể cho kết quả âm với số bị chia âm. Đây là bẫy đáng nhớ khi chuyển ngôn ngữ.

Ứng dụng 3: đổi đơn vị thời gian

giay = 3725
gio = giay // 3600
phut = (giay % 3600) // 60
con_lai = giay % 60
print(f"{gio}h {phut}m {con_lai}s")   # 1h 2m 5s

Chia nguyên lấy phần lớn, modulo lấy phần dư để chuyển sang đơn vị nhỏ hơn. Cách làm này dùng cho đổi tiền, đổi phút ra giờ, phân trang.

Ứng dụng 4: tách chữ số

def tong_chu_so(n):
    tong = 0
    while n > 0:
        tong += n % 10   # chữ số cuối
        n //= 10         # bỏ chữ số cuối
    return tong

print(tong_chu_so(1234))   # 10

Ứng dụng 5: chia đều và phân vùng

  • Bảng băm: dùng modulo để chuyển giá trị băm thành chỉ số ô.
  • Chia việc cho các worker: công việc số i giao cho worker i % số_worker.
  • Hiệu ứng xen kẽ: tô màu hàng chẵn lẻ của bảng.
  • Mã hóa đơn giản: mật mã Caesar dịch chữ cái và quay vòng ở 26.
def caesar(van_ban, dich):
    ket_qua = ""
    for c in van_ban:
        if c.isalpha() and c.isascii():
            goc = ord("A") if c.isupper() else ord("a")
            ket_qua += chr((ord(c) - goc + dich) % 26 + goc)
        else:
            ket_qua += c
    return ket_qua

print(caesar("Hello", 3))   # Khoor

Lỗi thường gặp

  1. Chia cho 0 khi modulo bằng 0 gây lỗi; luôn đảm bảo số chia khác 0.
  2. Quên hành vi khác nhau với số âm giữa các ngôn ngữ.
  3. Nhầm giữa / (chia thực) và // (chia nguyên) trong Python.

Bài tập củng cố modulo

  1. Lịch tuần. Cho ngày hôm nay là thứ Tư (đánh số 2 nếu thứ Hai là 0). Sau 100 ngày là thứ mấy?
  2. Đếm vòng tròn. 10 người đứng vòng tròn, đếm từ 1, cứ người thứ 3 bị loại. Ai là người cuối? (Bài Josephus: mô phỏng bằng danh sách và modulo.)
  3. Kiểm tra số Armstrong hoặc đảo ngược số bằng cách tách chữ số.
ten_thu = ["Hai", "Ba", "Tư", "Năm", "Sáu", "Bảy", "CN"]
hom_nay = 2                      # Thứ Tư
print(ten_thu[(hom_nay + 100) % 7])   # Thứ Sáu

nguoi = list(range(1, 11))
i = 0
while len(nguoi) > 1:
    i = (i + 2) % len(nguoi)
    nguoi.pop(i)
print(nguoi[0])   # 4

Ở bài thứ nhất, 102 chia 7 dư 4 nên rơi vào vị trí thứ năm trong danh sách, tức "Sáu" (thứ Sáu). Ở bài Josephus, mỗi lần loại một người, vị trí đếm tiếp theo được tính bằng modulo trên số người còn lại. Hãy chạy thử với các kích thước khác nhau và quan sát quy luật.

Kết luận

Modulo là chiếc "dao đa năng" nhỏ gọn của lập trình viên. Hãy thử giải FizzBuzz, đảo số, đổi giây ra giờ phút giây rồi làm mật mã Caesar để làm quen. Khi gặp bài toán về chu kỳ, vòng lặp quay vòng hoặc chia nhóm, hãy nghĩ ngay đến dấu phần trăm.

Nếu cần một lộ trình rõ ràng thay vì tự mò mẫm, codebem.com có nhiều khóa học phù hợp cho người mới.

author_photo
Nguyễn Đình Cường

0 comment