数论基础(更新版)
数论基础(更新中)
标签: 算法笔记 数论
入门知识
本单元难度\(\le\)小学六年级数学。
1.整数除法
除法是四则运算运算之一,作为乘法的逆运算。已知积与其中一个因数求另一因数的运算叫做除法.
整数除法常有如下表达:
\[a \div b = c \cdots d\]
一般地,我们称 a 为被除数,b 为除数,c 为商,d 为余数.
亦可简单推出如下逆运算:
\[b \times c + d = a\]
2.整除
如果 a 能把 b 除尽,也就是\(a \div b\)余数为0,则我们称 a 整除 b ,也称 b 被 a 整除.
记为:
\[a|b\]
中间的竖杠表示为整除符号,读作:a 整除 b.
数论之路,皆由“整除”始。
3.整除的性质
自反性
对于任意n,有\(n|n\).传递性
对于任意 \(a|b,b|c\),都有\(a|c\).
4.约数与倍数
如果\(a|b\),那么称 a 是 b 的约数,b 是 a 的倍数。同时称,a 是 b 的因子(因数)。
因此,我们有一个重要推论:
对于任何整数\(n \ge 2\),\(n\)至少有两个因子:1和 \(n\)(它本身).
我们将这两个因子称为\(n\)的平凡因子.
quiz1.如何计算\([1, n]\)中每个数因数的个数?
int p_num[MAXN];
for(int i = 1; i <= n; i ++)
for(int j = i; j <= n; j += i)
p_num[j] ++;
//O(nlogn)
5.质数
一个整数不存在非平凡因子,我们就称它为质数(亦称为素数).
不是质数的整数我们称它为合数,即合数有大于等于一个非平凡因子.
特殊地,1 不是质数,是合数.
例如:
2 只存在两个平凡因子,即1 和2,不存在非平凡因子.2 是质数.
5 只存在两个平凡因子,即1 和5,不存在费平凡因子.5 是质数.
4 存在非平凡因子 2. 4 不是质数,是合数.
1e9+7 不存在非平凡因子.1e9+7 是质数.
数论都是围绕质数概念所展开,理解质数是走进数论大厦的第一步。
6.判断质数
对于任意整数\(n\),判断在\([2, \sqrt{n}]\) 是否存在整数\(i\),使得\(i | n\).
存在这样的整数\(i\),\(n\)是合数.
否则,\(n\) 即为质数.
证明:
对于任何非平方数 \(n\),它的因子一定成对出现.
对于平方数 \(n\),除因子\(\sqrt{n}\)外,其余因子亦成对出现.
假设\(n\)存在一个因子 \(i\) 落在 \((\sqrt{n}, n)\) 区间,与 \(i\) 相对应的 \(n\) 的另一个因子 \(n / i\) 一定落在 \([2, \sqrt{n})\)这个区间。因为因子成对出现,所以只要判断\([2, \sqrt{n}]\) 这个区间是否存在因子就可以。
bool is_prime(int n)
{
if(n == 1) return false; //1是合数.
for(int i = 2; i * i <= n; i ++) //在[2, sqrt(n)]的区间中找因子.
if(!(n % i))
return false;
return true; //没找到因子是质数.
}
//O(sqrt(n))
quiz 2 质数有无限个。
如何证明?
反证法:
假设质数的个数为有限个,分别为:\(p_1,p_2,p_3,\cdots,p_n\).
设\(M = p_1 \times p_2 \times p_3 \times \cdots \times p_n + 1\).
易得,\(M \mod p_1 = 1, M \mod p_2 = 1, M \mod p_3 = 1, \cdots , M \mod p_n = 1\).
所以,\(M\)只存在\(1, M\)两个因子,即\(M\)也是质数,但\(M \notin \{i \leq n | q_i \}\).与假设矛盾.
所以,假设结论的逆命题,质数的个数为无限个成立.
tip 质数的一点小性质
虽然我们目前没有摸清楚质数的具体分布情况,但是我们能给出近似的:
设\(\pi (n)\)为不超过\(n\) 的质数个数:\[\pi(n) \sim \frac{n}{\ln{n}}\]
7.如何求质数?
- 朴素法
运用#6 如何判断质数的方法,对区间每个数进行枚举,然而这个方法实在算不上高效,复杂度是\(\sum_{i=1}^{n}\sqrt{i} = O(n\sqrt{n})\)
int isprime[MAXN], prime[MAXN], cnt = 0;
isprime[1] = 1;
for(int i = 2; i <= N; i ++)
for(int j = 2; j * j <= i; j ++)
if(!(i % j)){
isprime[i] = 1;
break;
}
for(int i = 1; i < N; i ++)
if(!isprime[i])
prime[++ cnt] = i;
//O(n * sqrt(n))
- 埃氏筛法
何为筛法?筛,就是筛选的意思。在一个区间中筛选出不是素数的数来,剩下的数就是素数.
首先找到第一个素数\(2\),用\(2\) 筛掉区间中所有\(2\) 的倍数(这些倍数因为已经有费平凡因子了,就一定是合数.),再用第二个素数\(3\) 筛掉区间中所有\(3\) 的倍数.当询问到\(4\) 的时候,因为\(4\) 已经被\(2\) 筛掉了,所以\(4\) 不是质数.第三个质数是\(5\),以此类推.筛掉区间的所有合数.
埃氏筛法用到了筛法的思想,时间复杂度虽然差于线性筛(一个合数可能被多个质数筛过),但也已经非常接近\(O(n)\)了.
int isprime[MAXN], prime[MAXN], cnt = 0;
isprime[1] = 1;
for(int i = 2; i <= N; i ++){
if(!isprime[i]){ //若是此数没有被之前任何一个素数筛掉,那么它就是质数.
prime[++ cnt] = i;
for(int j = 2; i * j <= N; j ++) //接着用这个质数,筛掉它的倍数.
isprime[i * j] = 1;
}
}
//>O(n)
- 欧拉线性筛法
对于小范围质数来说(算法竞赛范围),欧拉筛法是一种很实用的素数求法.还可以用这种筛法筛出欧拉函数.
欧拉筛的思路也是筛掉合数,留下的没被筛掉的数就是质数.欧拉筛优于埃氏筛的地方在于欧拉筛保证每个数都只被其最小的质因数筛掉.因此每个数都只被筛掉一次,所以是线性的.
int isprime[MAXN], prime[MAXN], cnt = 0;
for(int i = 2; i <= N; i ++){
if(!isprime[i])
prime[++ cnt] = i;
for(int j = 1; j <= cnt && i * prime[j] <= N; j ++){
isprime[i * prime[j]] = 1;
if(i % prime[j] == 0) //这个if语句是关键.当i中包含了一个质因数时,说明再往后的乘积
break; //的最小质因数就都是此刻i中包括的那个了质因数了,而不是之后的
} //prime[i]了,这就不满足每个合数只能被它最小的质因数筛掉的性质了.
}
//O(n)
最新编辑于2019/09/17 20:35
原文地址:https://www.cnblogs.com/satchelpp/p/11526773.html
上一篇: php良好代码风格的阶段性总结
下一篇: Golang 学习笔记(五):函数