数论入门
躺在草稿箱里的大一写的博客(个人感觉写的还行)
一、整除的性质
1. 整除:若a%b==0,则称a能被b整除或b能整除a,记作b | a.
2. 整除性质:
(1)若 a|b <=> -a|b <=> a|-b <=> |a| | |b|
(2)若 a|b,b|c ⇒ \Rightarrow ⇒ a|c
(3)若 a|b,a|c ⇒ \Rightarrow ⇒ a|(bx+cy) 其中 x,y 为任意整数
(4)若 a|b ⇒ \Rightarrow ⇒ am | bm 其中 m 为非零整数
(5)若 a|b,b|a ⇒ \Rightarrow ⇒ b = ±a <=> |b|=|a|
(6)若 a|bc,且 a 与 c 互质,则 a|b
(7)若a|b,a|c,且b与c互质,则a|bc
(8)若a|b,c为任意整数,则b|ac
二、模运算与同余运算
1.模运算及其性质
(1) 取模运算:a % p(a mod p),表示 a 除以 p 的余数。
(2) 模 p 加法:(a + b) % p = (a%p + b%p) % p
(3) 模 p 减法:(a - b) % p = (a%p - b%p) % p
(4) 模 p 乘法:(a * b) % p = ((a % p)*(b % p)) % p
(5) 幂模 p:(a^b) % p = ((a % p)^b) % p
(6) 模运算满足结合律、交换律和分配律。
(7) a≡b (mod n) 表示 a 和 b 模 n 同余,即 a 和 b 除以 n 的余数相等
注意:模运算无除法,不可使用(a / b) % p = (a%p / b%p) % p,遇到除法取模时,可求其乘法逆元
2.同余运算及其性质
(1)反身性:a≡a (mod m);
(2)对称性:若 a≡b(mod m),则 b≡a (mod m);
(3)传递性:若 a≡b(mod m),b≡c(mod m),则 a≡c(mod m);
(4)同余式相加:若 a≡b(mod m),c≡d(mod m),则 a±c≡b±d(mod m);
(5)同余式相乘:若 a≡b(mod m),c≡d(mod m),则 ac≡bd(mod m)。
三、快速幂
1. 快速幂
假设要求xn ,那么原题可以很轻松的表示为:xn= (((x2)2)2…)。这样只要做k次平方运算就能解决,时间复杂度就从O(n)下降到 log(n),只要幂运算的幂可以写成 2k 的形式,就可以用上面的方法降低时间复杂度。所以我们可以将任意的实数n改写有限个2k的形式的相乘。
例如假设 xn 等于 322 。x = 3, n = 22 = 10110(B),则有322 = 316 × \times × 34 × \times × 32,即310110 = 310000 × \times × 300100 × \times × 300010。
代码样例:
typedef long long ll;
ll quick_pow(ll a, ll n) //快速幂 a^n
{
ll ans = 1, base = a;
while (n) {
if (n & 1) ans = ans * base;
base = base * base; //计算1所在的位置的取值
n >>= 1; //二进制右移
}
return ans;
}
快速幂取模:
typedef long long ll;
ll quick_pow(ll a, ll n, ll mod) //快速幂取模 a^n % mod
{
ll ans = 1, base = a;
while (n) {
if (n & 1) ans = ans * base % mod;
base = base * base % mod;
n >>= 1;
}
return ans;
}
- 另一种理解方法:
- 当b为偶数时,xn 可以转为 x^2 的 b 2 \frac{b}{2} 2b 次方。
- 当b为奇数时,xn 可以转为 x^2 的 b 2 \frac{b}{2} 2b 次方,再乘以 x
代码解释:
ll pow(ll a,ll n) //快速幂 a^n
{
ll ans = 1, base = a;
while(n){
if(n & 1) ans = ans * base ; //n为奇数
base = base * base ; //n为偶数时,底数平方
n >>= 1; //指数减半
}
return ans;
}
2.光速幂
四、最大公因数与最小公倍数
辗转相除法(欧几里得算法)
以除数和余数反复做除法运算,当余数为 0 时,取当前算式除数为最大公约数
- 引入定理: gcd(a,b) = gcd(b, a %b)
gcd代码:
int gcd(int a, int b) {
int Rem;
while (b > 0) {
Rem = a % b;
a = b;
b = Rem;
}
return a;
}
gcd代码简化:
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
定理:a、b 两个数的最小公倍数乘以它们的最大公约数等于 a 和 b 本身的乘积。
- lcm(a,b) * gcd(a,b) = a * b
在求 lcm 时,如果将 lcm 写成 a * b / gcd(a,b),a*b 可能会溢出,正确的方法应该是先除后乘,即:lcm(a,b) = a / gcd(a,b) * b
lcm代码:
int lcm(int a, int b) {
return a / gcd(a, b) * b;
}
多个数(a1, a2, a3…am)的 lcm ,先求出 a1,a2 的 lcm ,再求出 lcm(lcm(a1,a2), a3)……
五、素数与素数筛
1.素数
- 质数是指在大于 1 的自然数中,除了 1 和它本身以外不再有其他因数的自然数
判断素数代码:
bool is_prime(int num) {
for (int i = 2; i * i <= num; i++) {
if (num % i == 0)
return false;
}
return true;
}
2.素数筛
- 素数筛就是快速筛出素数的算法
1.1 埃筛
- 复杂度 O(nloglogn)
埃筛代码:
const int MAXN = 1e6 + 5;
bool is[MAXN];//is[i] 1 is prime , 0 is not a prime
int prime[MAXN], cnt = 0;
void getprime() {
memset(is, 1, sizeof(is));
is[0] = is[1] = 0;
for (int i = 2; i < MAXN; i++) {
if (is[i]) {
prime[cnt++] = i;
for (int j = 2; i * j < MAXN; j++) {
is[i * j] = 0;
}
}
}
}
1.2 线性筛
- 复杂度 O(n)
线性筛代码:
const int MAXN = 1e6 + 5;
bool is[MAXN];//is[i] 1 is prime , 0 is not a prime
int prime[MAXN], cnt = 0;
void getprime() {
memset(is, 1, sizeof(is));
is[0] = is[1] = 0;
for (int i = 2; i < MAXN; i++) {
if (is[i]) {
prime[cnt++] = i;
}
for (int j = 0; j < cnt; j++) {
if (i * prime[j] > MAXN) //判断数组下标是否越界
break;
is[i * prime[j]] = 0;
if (i % prime[j] == 0) //根据最小素数因子筛除,避免重复筛选
break;
}
}
}
六、逆元
1.欧拉定理
- 若 n,a 为正整数,且 n,a 互质,则:a ^ φ \varphi φ(n) ≡ 1(mod n)
2.费马小定理
- 如果 p 是一个质数,而整数 a 不是 p 的倍数,则有 a ^(p-1)≡1(mod p)