整除和同余
整除
整除的概念、素数和合数
素数是网络安全的基础
素数:即除自身和 1 以外,没有其他因数的数,如 3 只能被 1 和自身整除,那么 3 是一个素数,注意 1 不是素数
整除的表示,若 b 能够整除 a,c 不能整除 a,则记为
可以用一个递归算法实现
list res; // 一个全局的顺序表,用以记录找到的素数
bool divide_exactly(int n, list res){
for(int i: res){
if(n % i == 0){
return 1; // 被整除
}
}
return 0; // 不被 res 中所有数整除
}
void search_prime_number(int n){
if(n == 2){
res.add(2);
return;
}
int q = sqrt(n); // 取平方根
search_prime_number(q); // 递归求解
// 当 n 大于 2 时,根据小于 q 的所有素数,加入不被所有素数整除且小于 n 的数,即为 (q, n) 区间内的素数
for(int i = q+1; i <= n; i++){
if(divide_exactly(i, res)){
continue;
}
res.add(i); // 当 i 不被所有 (0, q) 区间内素数整除时,确定 i 为一个素数,添入 res
}
}
list get_prime_number(int n){
n = abs(n); // 取绝对值
if(n < 2){
return NULL;
}
search_prime_number(n);
return res;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
证明素数无限,反证法(Euclid 开创的先和)
设素数有限,共有 m 个,表示为 Pi(1 ≤ i ≤ m),设数 n 为
根据 Eratosthenes 筛法,当一个数不被任意小于等于 √n 的素数整除时,n 将是一个素数,在这里
最大公因子和最小公倍数
数 n 为 a 和 b 最大公因子,充要条件为
- n 整除 a 和 b
- a 和 b 的任意其他公因子整除 n
记作
- a 和 b 整除 m
- m 能够整除 a 和 b 的任意公倍数
记作
公因子的线性表示
定理1.2.2:任意两个整数的公因子可以被这两个整数线性表示,即
算术基本定理
算术基本定理:任意正整数都可以被表示为一个素数乘积,如
通过标准分解式,我们可以求得任意整数的公因子和公倍数
Euclid 算法和辗转相除法
欧几里得算法,不失一般性,设 b > a,则 a 和 b 的最大公因子满足
拓展 Euclid 算法:辗转相除法,比传统的欧几里得更快收敛,采用除法取余的形式得到新的 b ,设整数 a < b,则有
欧几里得算法求最大公因子
int euclid(int a, int b){
if(a == b) return a;
if(a == 1 || b == 1) return 1;
if(a > b) return euclid(a-b, b);
else return euclid(a, b-a)
}
2
3
4
5
6
扩展欧几里得算法(辗转相除法)求最大公因子
int extended_euclid(int a, int b){
if(a == b) return a;
if(a > b){
if(a%b == 0) return b;
return extended_euclid(a%b, b);
}else{
if(b%a == 0) return a;
return extended_euclid(a, b%a);
}
}
2
3
4
5
6
7
8
9
10
使用辗转相除法求得最大公因子并把最大公因子用原整数线性表示
数论函数及定理
数论函数,就是一些规定好的函数变换,这里引入一个函数的性质:积性
设有一元函数 f,当自变量 m 和 n 为整数且互质时,满足
当 m 和 n 在任何情况下均满足上式(即无需互素),则称函数 f 完全积性
欧拉函数和定理
Euler
欧拉函数 φ(n),记录小于 n 的与 n 互素的正整数的个数,如
当 n 为合数时,一定可以被表示为有限个素数乘积,其 φ(n) 值通过这些素数的 φ(p) 直接相乘可以得到,又素数的 φ(p) 值为 p-1
当 n 为一个指数,其 φ(n) 值为这个指数的低一阶,如
威尔逊定理
当 p 是一个质数,则其减一的阶乘模自身将余 -1(等价于余 p-1)
费马小定理
当 p 为素数,则
同余
同余的定义和性质
同余的概念,设有整数 a 和 b,除数 m,若
是这样,我们把模操作视作一个周期函数,若 a 和 b 的差值刚好被 m 整除,那就说明 a 和 b 刚好跨越 m 模运算的一个周期倍数 t(t = a-b),故二者的模运算值一定相等,f(t+x) = f(x)
但可以确定的是,a 和 b 互为模 m 的一个余数,即
- 自反性:整数自己和自己同余
- 对称性:a 与 b 相互同余
- 传递性:a 与 b 同余,b 与 c 同余,则 a 与 c 同余
同余的加减乘除
辗转相除求解逆元
当 (a, m) = 1 时,这个最大公因子可以表示为
剩余类和剩余系
同余类是剩余类的集合,剩余类是一个数集,剩余系是 m 个剩余类中各抽出一个元素组成的数集
对于整数 m 来说,他将整个整数范围分为 m 各剩余类,即
当 [i] 中 i 与模数 m 互素时,我们称 [i] 是一个缩同余类,已知缩同余类的个数等于 φ(m)(即欧拉函数的值)
模指数运算
原理
int modulo(int a, int n, int m){ // a为底数,n为指数,m为模数
int c = 1;
for(int i = 0; i < n; i++){
if(c > m){
c = c%m;
}
c *= a%m;
}
return c;
}
2
3
4
5
6
7
8
9
10
显然这样的时间复杂度为 O(n)
将指数由二进制表示
b1 = [1,0,1,0], b2 = [1,1,1,0]
原指数可表示为
// 模指数运算
int modulo_exponential_operations(int a, int n, int m){
chat* b;
atoi(n, b, 2); // atoi 函数
int c = 1;
for(char i: b){
// 每步都要取模,防止溢出
c = (c^2) % m;
if(i == '1') // 当当前位置二进制为 1,乘上一个底数 a
c = (c*a) % m;
}
return c;
}
2
3
4
5
6
7
8
9
10
11
12
13
这样运算的时间复杂度降低到 O(logn)
在手动运算时,按照以下步骤求解
- 计算指数的二进制,得到所需数组 b
- 设 c 初始值为 1,循环平方并且遍历数组 b,b[i] 为 1 时乘以底数 a
即手动执行上述程序(模拟过程),即可求解
举个栗子