Bài tập chia dư với số mũ cực lớn C++

Ban đầu em làm theo cách tính số b tường minh ra nhưng đến 3 test cuối sẽ bị sai, sau đó em nghĩ theo hướng tính số b dần dần rồi chia mod sau mỗi lần tính thì đã đúng được 1 test số lớn nhưng mà các test khác lại sai hết, e ngồi nguyên ngày hqua với sáng hôm nay thực sự rất bí kh biết hỏi ai nên mong mng có thể giải đáp ạ .
code của e đây ạ: Paste ofCode

int powMod(int a,int b){
    int ans=1;
    for(int i=1;i<=b;i++){
        ans=ans*a;
        ans=ans%1337;
    }
    return ans;
}
int superPow (int a, int b[], int len){
  int M=b[0];
  int ans=powMod(powMod(a,M),10);
  for(int i=1;i<len;i++){
      ans*=powMod(a,b[i]);
      cout<<ans<<" ";
      ans=powMod(ans,10);
  }
    return ans;
}

2 bạn học cùng lớp à?

Thuật toán superPow của bạn bị sai rồi, bạn đọc topic trên tham khảo nhé.

1 Like

chắc cùng trường nhau a ạ, em cảm ơn anh

bài này dùng toán ko mệt mỏi lắm :pensive:

đầu tiên rút gọn a^b \equiv \alpha^b\mod n với \alpha = a \mod n

kế đến muốn áp dụng định lý Euler a^{\phi(n)} \equiv 1 \mod n cũng chưa được vì ở đây \alphan ko bảo đảm là số nguyên tố cùng nhau. Phải tách \alpha ra thành \alpha = 7^x * 191^y * \delta trong đó \deltan nguyên tố cùng nhau. Tách ra vậy rồi thì ta có

\begin{aligned} \alpha^b &\equiv (7^x * 191^y * \delta)^b \mod n \\ &\equiv (7^{xb} \mod n) * (191^{yb} \mod n) * (\delta^b \mod n) \mod n \\ &= p_1 * p_2 * p_3 \mod n \end{aligned}

để tính p_1 = 7^{xb} \mod n thì nếu x = 0 hay \alpha ko chia hết cho 7 thì p_1 = 1, còn x > 0 thì vì n chia hết cho 7 nên 7^k \mod n sẽ có chu kì :V, ở đây là chu kì có độ dài = 10:

7^0 mod 1337 = 1
7^1 mod 1337 = 7
7^2 mod 1337 = 49
7^3 mod 1337 = 343
7^4 mod 1337 = 1064
7^5 mod 1337 = 763
7^6 mod 1337 = 1330
7^7 mod 1337 = 1288
7^8 mod 1337 = 994
7^9 mod 1337 = 273
7^10 mod 1337 = 574
7^11 mod 1337 = 7     <-- lập lại 7^1 mod 1337

vậy có thể tính 7^{xb} \equiv 7^{xb \mod 10} \mod n, là 1 trong 10 số 7, 49, 343, …, 574 trên. Tính xb \mod 10 thì ở đây khá dễ, chỉ cần lấy phần tử cuối cùng trong mảng b là xong, vì xb \equiv x(b \mod 10) \mod 10.

để tính p_2 = 191^{yb} \mod n thì cũng tương tự như trên, y = 0 thì p_2 = 1, y > 0 thì vì n chia hết cho 191 nên 191^k \mod n sẽ có chu kì, ở đây là chu kì có độ dài = 3:

191^0 mod 1337 = 1
191^1 mod 1337 = 191
191^2 mod 1337 = 382
191^3 mod 1337 = 764
191^4 mod 1337 = 191     <-- lập lại 191^1 mod 1337

vậy có thể tính 191^{yb} \equiv 191^{yb \mod 3} \mod n, là 1 trong 3 số 191, 382, 764. Tính yb \equiv y(b \mod 3) \mod 3 thì ở đây cũng tương đối dễ :V, vì 10^i \equiv 1 \mod 3 nên chỉ cần lấy tổng các phần tử trong mảng b là rồi % 3 là được.

để tính p_3 = \delta^b \mod n thì áp dụng định lý Euler là được :V \delta^b \equiv \delta^{b \mod \phi(n)}\mod n với \phi(n) = \phi(1337) = \phi(7*191) = (7-1)(191-1) = 1140.

7 Likes

lục tung youtube của mấy anh Ấn độ mà mãi kh thông được hichic

a update công thức toán rồi đó :V

4 Likes

anh xem giúp em xem em sai chỗ nào với ạ. em làm theo thuật toán của a mà hình như do em làm sai chỗ p3 ấy

int superPow (int a, int b[], int len)
{
    int x=0,y=0,p1,p2,p3,c=0,d=0;
    while(a%7==0)
    {
        a/=7;
        x++;
    }
     while(a%191==0)
    {
        a/=191;
        y++;
    }
    p1=pow(7,x*b[len-1]);
    p1%=1337;
    for(int i=0;i<len;i++)
    c+=b[i];
    c%=3;
    p2=pow(191,y*c);
    p2%=1337;
    for(int i=0;i<len;i++)
    {
        d=(d*10+b[i])%1140;
    }
    p3=pow(a,d);
    p3%=1337;
    return (p1*p2*p3)%1337;
}

em printf thêm p1 p2 p3 thì n ra như này

a có giá trị trong khoảng [0, 1336]
d có giá trị trong khoảng [0, 1139]
a^d có thể lên tới 1336^1139 làm sao pow() tính nổi :V Xài hàm powMod() em viết lúc đầu ấy :V

chỗ này có thể sai nữa :V p1, p2, p3 có thể lên tới 1336, mà 1336^3 ~ 2.38 tỷ > 2^31 có thể vượt quá giới hạn của int 32-bit nên ko thể nhân p1*p2*p3 mà nên cast thành long long trước hoặc nhân p1 với p2 mod 1337 trước rồi nhân với p3 sau: (p1 * p2 % 1337) * p3 % 1337

chỗ này cũng có thể vượt quá độ chính xác nè. Phân tích \alpha < 1337 thành 7^x thì x có giá trị tối đa là 3, b[len-1] là chữ số có gtri tối đa là 9, vậy pow(7,x*b[len-1]) có gtri tối đa là 7^27 ~ 6x10^22 to quá kiểu double ko tính chính xác nổi đâu :V Phải lấy x*b[len-1] % 10 thì bảo đảm tối đa 7^9 pow() tính chính xác được. Nhưng lại vướng nếu x*b[len-1] % 10 == 0 mà x ban đầu khác 0 thì p1 ra 1 là sai, phải là 574 mới đúng. Lồng thêm cái if vào:

if (x == 0) {
    p1 = 1;
} else {
    p1 = pow(7, x * b[len-1] % 10);
    p1 %= 1337;
    if (p1 == 1) p1 = 574;
}

thêm cái if (y == 0) và if (p2 == 1) p2 = 764; với p2 nha :V

7 Likes

Hàm pow(x, y) cho kết quả là số thực, đâu có ra kết quả số nguyên chính xác đâu mà sao các bạn vẫn thích dùng nhỉ :stuck_out_tongue:

5 Likes

ehehe mới phát hiện ra có cách dễ hơn là xài https://en.wikipedia.org/wiki/Carmichael_function#Exponential_cycle_length

\lambda(n) = \lambda(1337) = LCM(\lambda(7), \lambda(191)) = LCM(\phi(7), \phi(191)) = LCM(6, 190) = 570 (LCM là BCNN - bội chung nhỏ nhất :V)

n = 1337 = 7^\textcolor{red}{1} * 191^\textcolor{red}{1}r_{max} = 1 nên ta có a \equiv a^{\lambda(n)+1} \mod n với mọi a luôn :V Tuy nhiên cái này ko rút gọn còn a^{\lambda(n)} \equiv 1 \mod n với mọi a được, ví dụ 7^0 \equiv 1 \mod 1337, nhưng 7^{570} \equiv 574 \mod 1337 :V

edit: :V :V :V
vì vấn đề ko rút gọn còn \equiv 1 như định lý Euler được nên nếu b chia hết cho 570 thì phải tính a^{570} \mod 1337 chứ ko tính a^0 \mod 1337 được nha :V
vậy phải tính

int bmod = b % 570;
if (bmod == 0) bmod = 570;
powMod(a % 1337, bmod);

mới đúng :V :V

cách này ngắn gọn mà nhiều chỗ hiểm quá =]]

6 Likes

Mò trên leetcode thì thấy có bài này, e để đây cho ai quan tâm ạ

1 Like
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?