Phép nhân Ấn Độ và Lũy thừa modulo

IV. Phép nhân Ấn Độ 1. Đặt vấn đề Xét một bài toán đơn giản như sau: Cho hai số a,b (a,b≤1018)a, btext{ }(a, b le 10^{18})a,b (a,b≤1018). Tính giá trị biểu thức (a×b) % 109(atimes b) text{ }%text{ } 10^9(a×b) % 109. Bài toán trên có thể dễ dàng giải quyết bằng tính chất phân phối của phép nhân đối

IV. Phép nhân Ấn Độ

1. Đặt vấn đề

Xét một bài toán đơn giản như sau: Cho hai số a,b (a,b≤1018)a, btext{ }(a, b le 10^{18}). Tính giá trị biểu thức (a×b) % 109(atimes b) text{ }%text{ } 10^9.

Bài toán trên có thể dễ dàng giải quyết bằng tính chất phân phối của phép nhân đối với phép đồng dư thức:

(a×b) % 109=[(a % 109)×(b % 109)] % 109(atimes b) text{ }%text{ } 10^9=[(atext{ }%text{ }10^9)times(btext{ }%text{ }10^9)]text{ }%text{ }10^9

Tuy nhiên, nếu như ta cần lấy số dư cho 101810^{18} thì sao? Phép toán bằng tính chất phân phối bây giờ sẽ không thể thực hiện được, vì [(a % 1018)×(b % 1018)]≤1036,[(atext{ }%text{ }10^{18})times(btext{ }%text{ }10^{18})]le10^{36}, dẫn đến kết quả của bước này sẽ bị vượt quá khả năng biểu diễn của kiểu số nguyên 6464 bit.

Phép nhân Ấn Độ sử dụng để tính (a×b) % M(atimes b)text{ }%text{ }M trong trường hợp tính chất phân phối với phép đồng dư thức không thể áp dụng được vì lí do tràn số.

2. Phép nhân Ấn Độ với đồng dư thức

Nguyên lí phép nhân Ấn Độ rất đơn giản như sau:

Dựa trên lý thuyết này, ta sẽ kết hợp phép nhân Ấn Độ với tính chất phân phối của phép nhân với phép đồng dư thức để tính được (a×b) %M(a times b) text{ }%M, với M≤1018M le 10^{18} mà không bị tràn số. Dưới đây là cài đặt sử dụng giải thuật chia để trị, áp dụng đệ quy:

longlongmultiply_modulo(longlong A,longlong B,longlong M){if(B ==0)return0;longlong T =multiply_modulo(A, B /2, M)% M;if(B &1)return((T + T)% M + A % M)% M;elsereturn(T + T)% M;}intmain(){int A, B, M;
    cin >> A >> B >> M;

    cout <<multiply_modulo(A, B, M);}

Đánh giá độ phức tạp: Ở mỗi lần gọi đệ quy, BB giảm đi một nửa, nên độ phức tạp của giải thuật là O(log⁡2(B))O(log_2(B)).

V. Lũy thừa Modulo

1. Giải thuật chia để trị

Dựa trên tư tưởng phép nhân Ấn Độ, ta có thể điều chỉnh công thức một chút để tính được lũy thừa ab %M,a^b %M, với a,b,M≤109a, b, M le 10^9. Công thức đơn giản như sau:

Cài đặt:

intpower_modulo(int A,int B,int M)// Tính A^B % M, {if(B ==0)return1LL;int half =power_modulo(A, B /2, M)% M;if(B &1)return(half * half * A)% M;elsereturn(half * half)% M;}

Đánh giá độ phức tạp: Ở mỗi lần gọi đệ quy, BB giảm đi một nửa, nên độ phức tạp của giải thuật là O(log⁡2(B))O(log_2(B)).

2. Tính AB % MA^Btext{ }%text{ }M với M≤1018M le 10^{18}

Trong trường hợp M≤1018M le 10^{18}, dựa trên những gì đã phân tích ở phần I, phép nhân thông thường sẽ không thể áp dụng vì lí do xảy ra tràn số. Vì vậy, ta sẽ kết hợp thêm phép nhân Ấn Độ trong trường hợp này. Độ phức tạp sẽ trở thành O(log⁡2(B)2)O(log_2(B)^2)

Cài đặt:

longlongpower_modulo(longlong A,longlong B,longlong M){if(B ==0)return1LL;int half =power_modulo(A, B /2LL, M)% M;
    half =multiply_modulo(half, half, M);if(B &1)returnmultiply_modulo(half, A, M);elsereturn half;}

3. Tính AB % MA^B % M trong trường hợp BB là số lớn và MM là số nguyên tố:

Đối với các trường hợp BB là số lớn – hiểu là các số nằm ngoài khả năng lưu trữ của kiểu số trong C++ và phải lưu bằng kiểu chuỗi – khi đó giải thuật tính AB % MA^B % M sẽ trở nên hơi phức tạp nếu như chúng ta cài đặt bằng các phép toán số lớn. Tuy nhiên, trong trường hợp MM là một số nguyên tố, dựa vào một số tính chất số học, ta có thể thu gọn được việc tính toán như sau:

  • Thứ nhất, ta biết định lý nhỏ Fermat được phát biểu như sau: Nếu MM là một số nguyên tố thì:

  • Lại có: AB % M=(AM−1.AM−1…AM−1.AX) % M,A^B % M = (A^{M-1}.A^{M-1}…A^{M-1}.A^X) % M, với AM−1A^{M-1} lặp lại ⌊BM−1⌋left lfloor{frac{B}{M-1}} right rfloor lần và X=B % (M−1)X = B % (M-1). Từ đây suy ra:

Tới đây chúng ta có thể áp dụng lũy thừa modulo một cách bình thường mà không sợ bị tràn số. Tất nhiên vẫn sẽ cần lưu ý về giới hạn của MM để lựa chọn phép nhân thông thường hay phép nhân Ấn Độ. Việc cài đặt xin dành lại cho bạn đọc.

Tài liệu tham khảo:

Nguồn: viblo.asia

Bài viết liên quan

So sánh Webhook và API: Khi nào nên sử dụng?

Trong lĩnh vực công nghệ thông tin và phát triển phần mềm, Webhook và API là hai th

Những ngành nghề AI có thể thay thế dần trong tương lai.

Những ngành nghề AI có thể thay thế trong tương lai gần Dựa trên các báo cáo và

Tạo Subdomain miễn phí với is-a.dev 🤪

Cuối tuần mọi người thế nào, mình thì rảnh quá lướt Facebook, tớ tình cờ th

Dùng TailwindCSS v4 trong SpringBoot + JTE

Giới thiệu JTE là gì? JTE (Java Template Engine) là một template engine an toàn, nhẹ và