质数
质数的定义
质数,又称素数。
-
质数和合数是针对所有大于
1 的 “自然数” 来定义的(所有小于等于1 的数都不是质数)。 -
所有小于等于
1 的整数既不是质数也不是合数。 -
质数和素数都是同一种性质,只是叫法不同。
质数的判定
试除法
说明:
一个合数的约数总是成对出现的,如果
时间复杂度:
分解质因数
特别注意:分解质因数与质因数不一样!分解质因数是一个过程,而质因数是一个数。
基本定理:
-
一个合数分解而成的质因数最多只包含一个大于
\sqrt n 的质因数(反证法,若n可以被分解成两个大于\sqrt n 的质因数,则这两个质因数相乘的结果大于n ,与事实矛盾)。 -
当枚举到某一个数
i 的时候,n 的因子里面已经不包含2\sim i-1 里面的数,如果n\bmod i=0 ,则i 的因子里面也已经不包含2\sim i-1 里面的数,因此每次枚举的数都是质数。
唯一分解定理
算数基本定理(唯一分解定理):任何一个大于
这里
这样的分解称为
此定理可推广至更一般的交换代数和代数数论。
质因子(质因数)在数论里是指能整除给定正整数的质数。根据算术基本定理,不考虑排列顺序的情况下, 每个正整数都能够以唯一的方式表示成它的质因数的乘积。
从质因子角度定义素数
两个没有共同质因子的正整数称为互质。因为1没有质因子,1与任何正整数(包括1本身)都是互质。
只有一个质因子的正整数为质数。
筛质数
暴力筛法
对每个数,进行试除枚举,每次枚举时间复杂度为
朴素筛法
做法:把
原理:假定有一个数
补充:调和级数:调和级数是由调和数列各元素相加所得的和。中世纪后期的数学家 Oresme 证明了所有调和级数都是发散于无穷的。但是调和级数的拉马努金和存在,且为欧拉常数(以下公式中,
所以该算法的时间复杂度约为
埃氏筛
埃拉托色尼筛法是由古希腊人埃拉托色尼在公元 250 年发明的质数筛法,简称埃式筛。
- 质数定理:
1\sim n 中有n\over \ln n 个质数。
过程
① 先把
1 删除;
② 读取数列中当前最小的数2,再把2的倍数删除;
③ 读取数列中当前最小的数3,再把3的倍数删除;
④ 依次进行下去,直到把所求范围内的数均读取完。
时间复杂度:
由定理可知,
1\sim n 中有n\over \ln n 个质数;又
\lim\limits_{k\rightarrow +\infty}n + \frac n 2 + \frac n 3 + \dots + \frac n k = n\ln n ;总复杂度为 $O(n\log(\log n))$。
代码
bool a[N];
vector<int> prime;
void chkprime(int x) {
for(int i=2;i<=x;++i) a[i]=true;
for(int i=2;i<=x;++i)
if(a[i]) {
prime.push_back(i);
for(int j=i*2;j<=x;j+=i)
a[j]=false;
}
}
4. 欧拉筛
时间复杂度:
核心:
原理:
过程
对于每个数字,如果在遍历前未被标记过,则这个数字是质数。
对于所有数字(无论是质数还是非质数),都与现在已知的所有质数相乘,直到将要与他相乘的素数大于其最小质因子或所有素数都被乘完为止。
欧拉筛法(
3 |
代码
const int N = 1e7+5;
bitset<N> f;
vector<int> prime;
void euler(int n) {
for(int i=2;i<=n;++i) {
if(!f[i]) prime.push_back(i);
for(auto c:prime) {
if(i*c>n) break;
f[i*c]=1;
if(i%c==0) break;
}
}
for(auto c:prime) cout<<c<<' ';
}