Tác giả:
Reviewer:
Lớp bài toán liên quan đến số học là một lớp bài toán thường hay xuất hiện trong các kỳ thi học sinh giỏi môn Tin học. Để giải quyết hiệu quả những bài tập này, cần phải nắm vững những kiến thức toán học có liên quan. Bài viết sau đây sẽ cung cấp một số thông tin cơ bản nhất về modulo.
Dưới đây là một số khái niệm cơ bản liên quan đến phép chia
Trong ngôn ngữ C++, phép chia được biểu diễn bằng toán tử /
, còn phép lấy dư được biểu diễn bởi toán tử %
. Nếu hai toán hạng trong phép tính đều ở kiểu dữ liệu int
, kết quả cũng sẽ là kết quả int
, do đó a / b
là phép lấy thương (hay phần nguyên) của phép chia a
cho b
, còn a % b
là phép lấy phần dư.
Số học modulo hiện đại được nghiên cứu lần đầu bởi C.Gauss năm 1801. Hiện tượng đồng dư cũng thường xuất hiện trong đời sống, chẳng hạn trên chiếc đồng hồ, 15 giờ và 3 giờ cùng được biểu diễn bởi một vị trí kim giờ/phút.
Cho số nguyên . Nếu có hai số nguyên thoả mãn: thì ta nói và đồng dư theo modulo . Ký hiệu là .
Nói cách khác, nếu thì .
Trong ví dụ về đồng hồ nói trên, ta có .
Phép đồng dư có một số tính chất sau đây:
Về mặt cài đặt, đồng dư thức không có nhiều ý nghĩa thiết thực, tuy nhiên nó có vai trò quan trọng trong việc xây dựng lời giải và là nền tảng cho các định lý tiếp sau đây.
Số dư của một phép chia tất nhiên sẽ đồng dư với số bị chia theo modulo là số chia. Vì vậy, mọi tính chất của đồng dư thức đều áp dụng được cho phép toán lấy số dư:
Khi tính hiệu modulo, sẽ xảy ra tình trạng
Với phép luỹ thừa, ta sử dụng luỹ thừa nhị phân thông qua các phép nhân modulo để tính.
Một vấn đề thường gặp phải khi tính toán các bài có modulo là hiện tượng tràn số. Với int
. Vấn đề này có thể được giải quyết một cách đơn giản bằng cách dùng long long
; tuy nhiên nếu vẫn muốn sử dụng int
để tiết kiệm bộ nhớ, ta có thể sử dụng:
int ans = 1ll * a * b % M
Ngoài ra, ta có thể ép kiểu để long long
bằng cách dùng (long long)a
, miễn sao phần tử đầu tiên của phép tính là một số kiểu long long
.
Nhìn chung, nên cố gắng thêm % MOD
và 1ll *
vào mọi phép tính để tránh những sai lầm đáng tiếc.
Bài toán: Tính
Khác với phép cộng, trừ và nhân, dễ thấy không thể có
Để giải quyết bài toán này, chúng ta cần một hướng tiếp cận khác. Trong nhiều trường hợp,
Nghịch đảo modulo: Số tự nhiên
Ví dụ:
Nghịch đảo modulo
Ta có thể dùng nghịch đảo modulo để tính kết quả của phép chia modulo như sau:
Một cách làm phổ biến để tính nghịch đảo modulo là áp dụng định lý Euler (hoặc định lý Fermat nhỏ), sẽ được trình bày ở dưới đây.
Hàm phi Euler: Đây là khái niệm cốt lõi của định lý Euler.
Hàm phi Euler của số tự nhiên
Nếu
Định lý Euler: Nếu
Định lý Fermat nhỏ: Với
Tính nghịch đảo modulo: Để tính nghịch đảo modulo
Như vậy,
//Luỹ thừa nhị phân
int binPow(int x, int y, int mod) {
int ret = 0;
for (; y; y /= 2, x = 1ll * x * x % mod) {
if (y % 2) ret = 1ll * ret * x % mod;
}
return ret;
}
//Nghịch đảo modulo
int inv(int x, int mod) {
return binPow(x, mod - 2, mod);
}
//Phép chia modulo
int divMod(int a, int b, int mod) {
return 1ll * a * inv(b) % mod;
}
Chú ý: Kết quả của
Trường hợp
Trường hợp
Giả sử
Nếu ta chọn
//Vẫn là phép chia modulo
int divMod(int a, int b, int mod) {
return a % (1ll * b * mod) / b;
}
Một số lưu ý khi sử dụng công thức trên:
long long
nếu Thông thường, cứ khi nào gặp số to hơn long long
, đề bài thường bắt chúng ta lấy kết quả
Bạn đọc có thể tham khảo thêm các phép toán sau:
Thuật toán Euclid mở rộng dùng để tìm các bộ số nguyên
Bạn đọc có thể tham khảo chi tiết về thuật toán Euclid mở rộng tại bài viết này.
Định lý: Cho
có họ nghiệm duy nhất là:
trong đó
Chứng minh sự tồn tại: Do
Xét
Vậy
Chứng minh sự duy nhất: Giả sử phương trình có hai nghiệm là
Do
hay
Rất nhiều bài toán đố vui hay dân gian dẫn đến việc phải giải hệ phương trình đồng dư và dẫn đến định lý này, chẳng hạn có bài Hàn Tín điểm binh nổi tiếng. Còn trong lập trình thi đấu, ta không thường xuyên gặp những bài toán với yêu cầu rõ ràng như vậy. Tuy nhiên, định lý này và các kết quả trung gian sử dụng để chứng minh định lý lại là nền tảng để giải rất nhiều bài toán số học, như những bài có tag chinese remainder theorem
trên Codeforces.
Định lý thặng dư Trung Hoa cho phép ta nhanh chóng giải một hệ phương trình đồng dư với các modulo đôi một nguyên tố cùng nhau. Chỉ cần tính các
Đôi khi, ta gặp phải các bài toán mà trong đó các modulo không nguyên tố cùng nhau với nhau. Trong trường hợp này, ta xét từng cặp phương trình một. Giả sử ta có hệ
Từ hệ này, ta có thể đặt
Khi đã tìm được
Bài rất dễ vận dụng phép chia và đồng dư cơ bản:
Bài cần suy luận nhiều hơn bằng các định lý và kết quả đồng dư: