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)(a,b), where a>ba>b.

We have:

a÷b=qra=bq+rr=abqa \div b = q \ldots r\\ a = bq + r \\ r = a - bq

The Euclidean algorithm states that gcd(a,b)=gcd(b,r)\gcd(a,b)=\gcd(b,r).

Let dd be any common divisor of aa and bb, and let:

m=a÷d, n=b÷dm = a \div d,\ n = b \div d

Then:

a=dm, b=dnr=abq=dm(dn)q=d(mnq)a = dm,\ b = dn\\ \begin{align*} r &= a - bq\\ &= dm - (dn)q\\ &= d(m - nq) \end{align*}

Since r=d(mnq)r=d(m-nq), if aa and bb have any common divisor dd, then dd is also a common divisor of rr and bb (except when r=0r=0; in that case the greatest common divisor is aa). If ABAB is the set of common divisors of aa and bb, and RBRB is the set of common divisors of rr and bb, then ABRBAB \in RB when r0r\ne0.

This alone does not prove gcd(a,b)=gcd(b,r)\gcd(a,b)=\gcd(b,r), because RBRB might contain a number larger than gcd(a,b)\gcd(a,b). If we prove RBABRB\in AB, then AB=RBAB=RB, so RBRB cannot contain a number larger than gcd(a,b)\gcd(a,b).

Let ee be any number in RBRB. Then:

b=me,r=neb=me,\qquad r=ne

Substitute bb and rr back into a=bq+ra=bq+r:

a=bq+ra=(me)q+(ne)ra=(mq+nr)e\begin{align*} a &= bq + r\\ a &= (me)q + (ne)r\\ a &= (mq + nr)e \end{align*}

Thus eae\mid a, so every ee in RBRB is also in ABAB; that is, RBABRB\in AB. Therefore gcd(a,b)=gcd(b,r)\gcd(a,b)=\gcd(b,r).

The Euclidean algorithm is very concise in code:

1
2
3
4
5
6
int gcd(int a, int b){
if(b)
return gcd(b, a % b);
else
return a;
}

References:

  1. https://www.bilibili.com/video/BV19r4y127fu?spm_id_from=333.880.my_history.page.click&vd_source=4de003ee9a3815aedd7d0cb2c7a12d14
  2. https://www.bilibili.com/video/BV1my4y1z7Zn?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=4de003ee9a3815aedd7d0cb2c7a12d14
  3. https://www.cnblogs.com/zjp-shadow/p/9267675.html#扩展欧几里得

Extended Euclidean Algorithm (exgcd)

The extended Euclidean algorithm finds one solution to:

ax+by=gcd(a,b).ax+by=\gcd(a,b).

For example, the following Euclidean-algorithm calculation computes gcd(1180,482)=2\gcd(1180,482)=2:

gcd(a,b)=gcd(b,r)a=bq+r1180=482(2)+216482=216(2)+50216=50(4)+1650=16(3)+216=2(8)+0\begin{align*} \gcd(a,b)&=\gcd(b,r)\\ a&=bq+r\\ 1180&=482(2)+216\\ 482&=216(2)+50\\ 216&=50(4)+16\\ 50&=16(3)+2\\ 16&=2(8)+0 \end{align*}

We can derive a solution to 2=1180x+482y2=1180x+482y. Start with the penultimate step, 50=16(3)+250=16(3)+2, and rewrite it as:

2=50+16(3).2=50+16(-3).

Applying a=bq+rr=a+b(q)a=bq+r\to r=a+b(-q) to the preceding steps gives:

216=50(4)+1616=216+50(4)482=216(2)+5050=482+216(2)1180=482(2)+216216=1180+482(2)\begin{align*} 216=50(4)+16&\to16=216+50(-4)\\ 482=216(2)+50&\to50=482+216(-2)\\ 1180=482(2)+216&\to216=1180+482(-2) \end{align*}

Substitute these expressions into 2=50+16(3)2=50+16(-3). We can first replace the 1616 in the expression with the sum of 216216 and 50(4)50(-4).

The expression now has the form 2=216x+50y2=216x+50y, where x=3x=-3 and y=13y=13.

Next, replace 5050 with the sum of 482482 and 216(2)216(-2), turning the expression into 2=482x+216y2=482x+216y.

Finally, replace 216216 with the sum of 11801180 and 482(2)482(-2). The resulting expression is:

2=1180x+482y2=1180x+482y

where x=29x=-29 and y=71y=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)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)ax+by=\gcd(a,b)

Because gcd(a,b)=gcd(b,amodb)\gcd(a,b)=\gcd(b,a\bmod b), gcd(b,amodb)\gcd(b,a\bmod b) can also be written in the form ax+byax+by:

gcd(b,amodb)=bx2+(amodb)y2.\gcd(b,a\bmod b)=bx_2+(a\bmod b)y_2.

Although gcd(b,amodb)\gcd(b,a\bmod b) is equal to gcd(a,b)\gcd(a,b) here, giving

ax+by=bx2+(amodb)y2,ax+by=bx_2+(a\bmod b)y_2,

the two sides use the same general form but have different values in the positions of aa and bb. Their solutions x,yx,y and x2,y2x_2,y_2 are therefore different. Suppose we already know x2x_2 and y2y_2. If we can determine how to calculate xx and yy from them, we can solve xx and yy recursively.

We can simplify bx2+(amodb)y2bx_2+(a\bmod b)y_2:
Then:

ax+by=bx2+(amodb)y2=bx2+(aa/bb)y2=ay2+b(x2a/by2).\begin{align*} ax+by&=bx_2+(a\bmod b)y_2\\ &=bx_2+(a-\lfloor a/b\rfloor b)y_2\\ &=ay_2+b(x_2-\lfloor a/b\rfloor y_2). \end{align*}

Thus, if we have already found the solution (x2,y2)(x_2,y_2) to bx2+(amodb)y2=gcd(b,amodb)bx_2+(a\bmod b)y_2=\gcd(b,a\bmod b), then in the original expression ax+by=gcd(a,b)ax+by=\gcd(a,b) we have x=y2x=y_2 and y=x2a/by2y=x_2-\lfloor a/b\rfloor y_2. This gives a recursive solution.

The boundary condition is similar to the ordinary Euclidean algorithm: b=0b=0. Then:

ax+by=gcd(a,b)ax+(0)y=ax=1\begin{align*} ax+by &= \gcd(a,b)\\ ax+(0)y &= a\\ x &= 1 \end{align*}

Although yy 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};
}

References:

  1. https://zhuanlan.zhihu.com/p/86561431
  2. https://www.cnblogs.com/zjp-shadow/p/9267675.html#扩展欧几里得

Modular Multiplicative Inverse

The modular multiplicative inverse of amodpa\bmod p is the solution xx of ax1(modb)ax\equiv1\pmod b.

A modular inverse is somewhat like an additive inverse under a modulus.

exgcd

When aa and bb are coprime, exgcd can solve this problem. Since gcd(a,b)=1\gcd(a,b)=1, extended Euclid solves ax+by=1ax+by=1.

Rewrite ax1(modb)ax\equiv1\pmod b as:

ax1(modb)axmodb=1axbk=1.ax\equiv1\pmod b\\ ax\bmod b=1\\ ax-bk=1.

Setting y=ky=-k gives ax+by=1ax+by=1.

One of xx and yy in ax+by=1ax+by=1 may be negative. A negative yy causes no problem, but if xx is negative, the answer we obtain is not the smallest positive integer among all feasible values of xx.

Looking at ax+by=1ax+by=1, we can add a multiple of bb to xx, transforming the expression into a(x+bn)+b(y+an)=1a(x+bn)+b(y+an)=1 (note that bb is negative, so the abnabn terms cancel). This lets us make xx positive without changing the equality ax+by=1ax+by=1.

We can therefore write:

1
x = (x % b + b) % b;

Assume first that xx is negative.

The first x % b adds some multiples of bb to xx, turning it into the largest negative number that still satisfies the condition. For example, suppose bb is 13 and xx is -25. After x = x % b, xx becomes -12, which is equivalent to adding 13 to xx.

The following +b turns this largest valid negative number into the smallest valid positive number. For example, x+b=12+13=1x+b=-12+13=1. What, then, is the purpose of the final % b?

It handles the case where xx was positive to begin with. By subtracting suitable multiples of bb from xx, it reduces xx to the smallest positive number satisfying the condition.

For the modular-inverse template problem:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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};
}
int main() {
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 3e63e6 and the time limit is 500 ms, an nlogpn\log p algorithm is too slow; use the linear algorithm below.

References:

  1. https://www.cnblogs.com/zjp-shadow/p/7773566.html
  2. https://zhuanlan.zhihu.com/p/86561431

Linear Recurrence

The linear recurrence computes the modular inverses of all integers from 11 to nn modulo a prime pp in O(n)O(n) time. pp 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=11\times1=1. This gives us the initial condition.

Suppose the recurrence has now reached the number ii. Write p÷i=krp\div i=k\ldots r, so p=ki+rp=ki+r. Converting this to a congruence gives:

ki+r0(modp).ki+r\equiv0\pmod p.

Let i1i^{-1} and r1r^{-1} denote the modular inverses of ii and rr modulo pp, respectively. Multiplying both sides of the congruence by i1r1i^{-1}r^{-1} and expanding gives:

kr1+i10(modp),kr^{-1}+i^{-1}\equiv0\pmod p,

so:

i1kr1(modp).i^{-1}\equiv-k r^{-1}\pmod p.

The simplification uses i1i1(modp)i^{-1}i\equiv1\pmod p, with the same relationship holding for rr and r1r^{-1}. Since k=p/ik=\lfloor p/i\rfloor and r=pmodir=p\bmod i:

i1p/i×(pmodi)1(modp).i^{-1}\equiv-\lfloor p/i\rfloor\times(p\bmod i)^{-1}\pmod p.

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;
}
int main() {
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';
}
}

Reference:

  1. https://zhuanlan.zhihu.com/p/86561431