【知识点】数论入门基础
预备知识
最大公约数
代码为:__gcd(a,b)
最小公倍数
代码为:a/__gcd(a,b)*b
互素
又名互质、既约。
gcd(a1,a2,a3,...,an) = 1则称a1到an所有数互素。
注意,多个数互素,不一定就两两互素。例如6,10,15互素,但两两都不互素。
素数与合数
设整数p满足p不为0、1、-1,若p没有平凡约数外的其他约数,则称为素数(质数)。
注意,一般情况下,若无特殊说明,素数指正的素数。
若整数p满足p不为0、1、-1,且p不是素数,则p为合数。
特殊性质:所有大于3的素数都可以表示为6n+1或6n-1的形式。
素数-素性测试
素性测试分两种:
- 确定性测试:绝对确定一个数是否为素数。
- 概率性测试:比确定性测试快很多,但得到的伪素数也可能是合数。
试除法
bool isPrime(int a) {
if (a < 2) return 0;
for (int i = 2; (long long)i * i <= a; ++i) // 防溢出
if (a % i == 0) return 0;
return 1;
}
Fermat素性测试
最简单的一种概率性测试方法,借助费马小定理。核心思想是:
不断选取[2,n-1]中的基a,并检验是否每次都有\(a^{n - 1} \equiv 1 \pmod{n}\).
bool millerRabin(int n) {
if (n < 3) return n == 2;
// test_time 为测试次数,建议设为不小于 8
// 的整数以保证正确率,但也不宜过大,否则会影响效率
for (int i = 1; i <= test_time; ++i) {
int a = rand() % (n - 2) + 2;
if (quickPow(a, n - 1, n) != 1) return false;
}
return true;
}
当然,即使满足了\(a^{n - 1} \equiv 1 \pmod{n}\)的n也不一定是素数。比如341=11*31 是合数,但选择a=2时,\(2^{340} \equiv 1 \pmod{341}\),这时n称为伪素数。
最大公约数算法
欧几里得算法
即辗转相除法。\(\gcd(a, b) = \gcd(b, a \bmod b)\)。
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
在输入两个长为n的二进制整数时,欧几里得算法的时间复杂度为O(n),换句话说,在默认a和b是同阶的情况下,时间复杂度为O(log max(a, b))。
更相减损术
gcd(a, b) = gcd(a-b, b)
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a - b);
}
更相减损术的时间复杂度为O(n)。
Stein算法
如果a>>b,那么更相损减术的时间复杂度将达到最坏情况。
进行优化:
如果2|a且2|b,则gcd(a, b) = 2gcd(a/2, b/2)。
否则如果2|a(2|b同理),则gcd(a, b) = gcd(a/2, b)。
Stein算法的时间复杂度为O(logn)。
求多个数的最大公约数
每次将前两项的最大公约数放回数列中,删除原两项,重复此操作直到数列中只剩下一项,即为最大公约数。
最小公倍数算法
两个数的最小公倍数
lcm(a, b) = (a * b) / gcd(a, b)
为了避免溢出,经常先除后乘来计算。
多个数的最小公倍数
每次将前两项的最小公倍数放回数列中,删除原两项,重复此操作直到数列中只剩下一项,即为最小公倍数。
扩展欧几里得算法
扩展欧几里得算法,常用于求ax + by = gcd(a, b)的一组可行解。
当b = 0时,解为x = 1, y = 0。
否则:
设
\(ax_1 + by_1 = \gcd(a,b)\)
\(bx_2 + (a \mod b)y_2 = \gcd(b, a \mod b)\)
由欧几里得定理可知:\(\gcd(a,b) = \gcd(b, a \mod b)\)
所以 \(ax_1 + by_1 = bx_2 + (a \mod b)y_2\)
又因为 \(a \mod b = a - \left(\left\lfloor \frac{a}{b} \right\rfloor \times b\right)\)
所以 \(ax_1 + by_1 = bx_2 + \left(a - \left\lfloor \frac{a}{b} \right\rfloor \times b\right)y_2\)
\(ax_1 + by_1 = ay_2 + bx_2 - \left\lfloor \frac{a}{b} \right\rfloor \times by_2 = ay_2 + b\left(x_2 - \left\lfloor \frac{a}{b} \right\rfloor y_2\right)\)
因为 \(a = a,b = b\),
所以 \(x_1 = y_2, y_1 = x_2 - \left\lfloor \frac{a}{b} \right\rfloor y_2\)
将x2, y2不断代入递归求解,直到回到x = 1, y = 0的基础情况。
代码实现:
int Exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1;
y = 0;
return a;
}
int d = Exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - (a / b) * y;
return d;
}
函数返回值为gcd。x, y为全局变量,运行结束后均已完成修改。
原方程的解有无数个,有的解会爆long long,而扩展欧几里得算法给出的解必有\(|x|<=b\), \(|y|<=a\)。

浙公网安备 33010602011771号