The content below was generated entirely by machine translation. Please verify its accuracy. If anything is unclear, consult the Chinese source version.
Euclidean Algorithm
Find the greatest common divisor (a,b), where a>b.
We have:
a÷b=q…ra=bq+rr=a−bq
The Euclidean algorithm states that gcd(a,b)=gcd(b,r).
Let d be any common divisor of a and b, and let:
m=a÷d,n=b÷d
Then:
a=dm,b=dnr=a−bq=dm−(dn)q=d(m−nq)
Since r=d(m−nq), if a and b have any common divisor d, then d is also a common divisor of r and b (except when r=0; in that case the greatest common divisor is a). If AB is the set of common divisors of a and b, and RB is the set of common divisors of r and b, then AB∈RB when r=0.
This alone does not prove gcd(a,b)=gcd(b,r), because RB might contain a number larger than gcd(a,b). If we prove RB∈AB, then AB=RB, so RB cannot contain a number larger than gcd(a,b).
Let e be any number in RB. Then:
b=me,r=ne
Substitute b and r back into a=bq+r:
aaa=bq+r=(me)q+(ne)r=(mq+nr)e
Thus e∣a, so every e in RB is also in AB; that is, RB∈AB. Therefore gcd(a,b)=gcd(b,r).
The Euclidean algorithm is very concise in code:
1 2 3 4 5 6
intgcd(int a, int b){ if(b) returngcd(b, a % b); else return a; }
Substitute these expressions into 2=50+16(−3). We can first replace the 16 in the expression with the sum of 216 and 50(−4).
The expression now has the form 2=216x+50y, where x=−3 and y=13.
Next, replace 50 with the sum of 482 and 216(−2), turning the expression into 2=482x+216y.
Finally, replace 216 with the sum of 1180 and 482(−2). The resulting expression is:
2=1180x+482y
where x=−29 and y=71.
This is exactly the answer we wanted.
It is apparent that exgcd is somewhat like running the Euclidean algorithm in reverse. It uses the calculation process of the Euclidean algorithm to derive one solution to ax+by=gcd(a,b).
Now let us generalize the pattern we just observed. What we want to solve first is:
ax+by=gcd(a,b)
Because gcd(a,b)=gcd(b,amodb), gcd(b,amodb) can also be written in the form ax+by:
gcd(b,amodb)=bx2+(amodb)y2.
Although gcd(b,amodb) is equal to gcd(a,b) here, giving
ax+by=bx2+(amodb)y2,
the two sides use the same general form but have different values in the positions of a and b. Their solutions x,y and x2,y2 are therefore different. Suppose we already know x2 and y2. If we can determine how to calculate x and y from them, we can solve x and y recursively.
Thus, if we have already found the solution (x2,y2) to bx2+(amodb)y2=gcd(b,amodb), then in the original expression ax+by=gcd(a,b) we have x=y2 and y=x2−⌊a/b⌋y2. This gives a recursive solution.
The boundary condition is similar to the ordinary Euclidean algorithm: b=0. Then:
ax+byax+(0)yx=gcd(a,b)=a=1
Although y can take any value in this case, we normally return 0.
The following is the code, using the C++20 standard:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
template<typename T> concept Integral = std::is_integral<T>::value; // gcd, x, y template<Integral T> tuple<T, T, T> ex_gcd(T a, T b){ if (b == 0) { return {a, 1, 0}; } auto [gcd, x2, y2] = ex_gcd(b, a % b); // Derive x and y from x2 and y2. T x = y2; T y = x2 - (a / b) * y2; return {gcd, x, y}; }
The modular multiplicative inverse of amodp is the solution x of ax≡1(modb).
A modular inverse is somewhat like an additive inverse under a modulus.
exgcd
When a and b are coprime, exgcd can solve this problem. Since gcd(a,b)=1, extended Euclid solves ax+by=1.
Rewrite ax≡1(modb) as:
ax≡1(modb)axmodb=1ax−bk=1.
Setting y=−k gives ax+by=1.
One of x and y in ax+by=1 may be negative. A negative y causes no problem, but if x is negative, the answer we obtain is not the smallest positive integer among all feasible values of x.
Looking at ax+by=1, we can add a multiple of b to x, transforming the expression into a(x+bn)+b(y+an)=1 (note that b is negative, so the abn terms cancel). This lets us make x positive without changing the equality ax+by=1.
We can therefore write:
1
x = (x % b + b) % b;
Assume first that x is negative.
The first x % b adds some multiples of b to x, turning it into the largest negative number that still satisfies the condition. For example, suppose b is 13 and x is -25. After x = x % b, x becomes -12, which is equivalent to adding 13 to x.
The following +b turns this largest valid negative number into the smallest valid positive number. For example, x+b=−12+13=1. What, then, is the purpose of the final % b?
It handles the case where x was positive to begin with. By subtracting suitable multiples of b from x, it reduces x to the smallest positive number satisfying the condition.
int n, p; template<typename T> concept Integral = std::is_integral<T>::value; template<Integral T> tuple<T, T, T> ex_gcd(T a, T b){ if (b == 0) return {a, 1, 0}; auto[gcd, x2, y2] = ex_gcd(b, a % b); T x = y2; T y = x2 - (a / b) * y2; return {gcd, x, y}; } intmain(){ cin>>n>>p; for(int i = 1; i <= n; i++){ auto[gcd, x, y] = ex_gcd(i, p); x = (x % p + p) % p; cout<<x<<endl; } }
Because the data size is 3e6 and the time limit is 500 ms, an nlogp algorithm is too slow; use the linear algorithm below.
The linear recurrence computes the modular inverses of all integers from 1 to n modulo a prime p in O(n) time. p must be prime to ensure all values in the range are coprime to it.
Because this is a recurrence algorithm, it needs an initial condition. It is easy to see that the inverse of 1 modulo any integer is 1 itself, because 1×1=1. This gives us the initial condition.
Suppose the recurrence has now reached the number i. Write p÷i=k…r, so p=ki+r. Converting this to a congruence gives:
ki+r≡0(modp).
Let i−1 and r−1 denote the modular inverses of i and r modulo p, respectively. Multiplying both sides of the congruence by i−1r−1 and expanding gives:
kr−1+i−1≡0(modp),
so:
i−1≡−kr−1(modp).
The simplification uses i−1i≡1(modp), with the same relationship holding for r and r−1. Since k=⌊p/i⌋ and r=pmodi:
i−1≡−⌊p/i⌋×(pmodi)−1(modp).
Normalize the possibly negative value in the same way:
1
x = (x % b + b) % b;
The template code is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
ll inv[MAXN]; template <typename T> concept Int_t = is_integral<T>::value; template <Int_t T> inline T mod_norm(T val, T m){ return (val % m + m) % m; } intmain(){ ios::sync_with_stdio(0); cin.tie(0); ll n, p; cin >> n >> p; inv[1] = 1; cout << inv[1] <<'\n'; for (int i = 2; i <= n; i++) { inv[i] = mod_norm(-p / i * inv[p % i] % p, p); cout << inv[i] <<'\n'; } }