Skip to content

Commit ba54ae6

Browse files
committed
Add fast GCD algorithm
1 parent 3cd1128 commit ba54ae6

File tree

1 file changed

+114
-0
lines changed

1 file changed

+114
-0
lines changed

source/_posts/fast-gcd.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
---
2+
categories:
3+
- OI
4+
title: 快速 GCD 算法
5+
date: 2024-07-25 17:16:27
6+
tags:
7+
- math
8+
---
9+
10+
> 例题:[Luogu P5435](https://www.luogu.com.cn/problem/P5435)
11+
12+
## 预处理 GCD
13+
14+
一种 `$O(n)$` 预处理,`$O(1)$` 查询两个小于 `$n$` 的数的快速 `$\gcd$`
15+
16+
### 引理
17+
18+
对于任意正整数 `$n$`,可以将 `$n$` 写成 `$n=abc$`,满足 `$a,b,c$` 任意一个小于 `$\sqrt{n}$` 或为质数。
19+
20+
考虑归纳,首先对于 `$1$`,显然成立。
21+
22+
否则,设 `$n$` 的最小质因子为 `$p$`,设 `$\dfrac{n}{p}=xyz$` 是一个合法表示,不妨设 `$x\le y\le z$`。若 `$x=1$`,则 `$n=pyz$` 是一个合法表示。不然有 `$p\le x\le y\le z$`,则 `$p^4\le n$`。若 `$xp\gt\sqrt{n}$`,有 `$yp\gt\sqrt{n},zp\gt\sqrt{n}$`,则 `$xyzp^3\gt n^{3/2}$`,矛盾,因此 `$n=(xp)yz$` 是合法表示。
23+
24+
注意这个证明给出了合法表示的构造方法。
25+
26+
### 预处理
27+
28+
+ 预处理所有 `$(i,j)$`,其中 `$i,j\le \sqrt{n}$``$\gcd$`。显然可以 `$O(n)$` 推出来。
29+
+ 预处理 `$n$` 以内质数。
30+
31+
### 查询
32+
33+
计算 `$\gcd(x,y)$`
34+
35+
`$x=abc$`,则:
36+
37+
$$(x,y)=(a,y)\times(b,\dfrac{y}{(a,y)})\times(c,\dfrac{y}{(ab,y)})$$
38+
39+
`$a\in\mathbf{P}$`,只需判断 `$a$` 是否整除 `$y$`,否则 `$(a,y)=(y\bmod a,a)$`,因为 `$a\le\sqrt{n}$`,可以查表。
40+
41+
### 实现
42+
43+
```cpp
44+
const int T = 1000;
45+
const int M = 1000000;
46+
47+
int g[T][T], fac[M][3];
48+
bitset<M> vis;
49+
vector<int> pri;
50+
void prework() {
51+
vis[0] = vis[1] = true;
52+
fac[1][0] = fac[1][1] = fac[1][2] = 1;
53+
for (int i = 2; i < M; ++i) {
54+
if (!vis[i]) {
55+
fac[i][0] = fac[i][1] = 1;
56+
fac[i][2] = i;
57+
pri.push_back(i);
58+
}
59+
for (int j : pri) {
60+
int mul = i * j;
61+
vis[mul] = true;
62+
fac[mul][0] = fac[i][0] * j;
63+
fac[mul][1] = fac[i][1];
64+
fac[mul][2] = fac[i][2];
65+
sort(fac[mul], fac[mul] + 3);
66+
if (i % j == 0)
67+
break;
68+
}
69+
}
70+
for (int i = 0; i < T; ++i)
71+
g[0][i] = g[i][0] = i;
72+
for (int i = 1; i < T; ++i)
73+
for (int j = 1; j <= i; ++j)
74+
g[i][j] = g[j][i] = g[j][i % j];
75+
}
76+
int gcd(int a, int b) {
77+
int ans = 1;
78+
for (int i = 0; i < 3; ++i) {
79+
int _ = fac[a][i] > T ? (b % fac[a][i] ? 1 : fac[a][i])
80+
: g[fac[a][i]][b % fac[a][i]];
81+
b /= _;
82+
ans *= _;
83+
}
84+
return ans;
85+
}
86+
```
87+
88+
## 更相减损术
89+
90+
小常数 `$O(\log n)$`。
91+
92+
+ 若 `$a=b$`,`$(a,b)=a=b$`
93+
+ 若 `$2\mid a,2\mid b$`,`$(a,b)=\left(\dfrac{a}{2},\dfrac{b}{2}\right)$`。
94+
+ 若 `$2\mid a,2\nmid b$`(反之同理),`$(a,b)=\left(\dfrac{a}{2},b\right)$`。
95+
+ 若以上均不满足,设 `$a>b$`,则 `$(a,b)=(a-b,b)$`。
96+
97+
```cpp
98+
int gcd(int a, int b) {
99+
int i = __builtin_ctz(a);
100+
int j = __builtin_ctz(b);
101+
int k = min(i, j);
102+
int d;
103+
b >>= j;
104+
while (a) {
105+
a >>= i;
106+
d = b - a;
107+
i = __builtin_ctz(d);
108+
if (a < b) b = a;
109+
if (d < 0) a = -d;
110+
else a = d;
111+
}
112+
return b << k;
113+
}
114+
```

0 commit comments

Comments
 (0)