目录
数学知识
质数
定义:在大于1的整数中,如果只包含1和本身这两个约数,就被称为质数(老版本叫素数)。
质数的判定——试除法(时间O( sprt(n) ))
——例题1:AcWing 866. 试除法判定质数
给定n个正整数ai,判定每个数是否是质数。
输入格式
第一行包含整数n。
接下来n行,每行包含一个正整数ai。
输出格式
共n行,其中第 i 行输出第 i 个正整数ai是否为质数,是则输出“Yes”,否则输出“No”。
数据范围
1≤n≤100,
1≤ai≤2∗109
输入样例:
2
2
6
输出样例:
Yes
No
————思路:在2 <= i <= n / i中找约数
bool is_prime(int n){//朴素判定(暴力)
if(n < 2) return 0;//小于2的数不在范围内,直接排除
for(int i = 2; i < n; i ++)//枚举从2到n-1
if(n % i == 0)//如果可以整除某一个数
return 0;//就说明不是质数
return 1;
}
时间复杂度是O(n),效率低。
优化:1,约数是一对一对的,所以每次枚举较小的一个约数就好
n / d = 整数
=> n / (n/d) = 整数
枚举较小的一个约数即:枚举 d <= (n / d)
=> d <= sqrt(n)
=> d <= n / d
时间O(sqrt(n))
————总而言之:d <= n / d 当中一定可以找到所有的约数组当i <= n/i 时,说明还没有遍历到重复的约数组
bool is_prime(int n){//优化写法
if(n < 2) return 0;//小于2的数不在范围内,直接排除
for(int i = 2; i <= n / i; i ++)//当i <= n/i 时,说明还没有遍历到重复的约数组
/*注意不要写成for(int i = 2; i * i <= n; i ++),会溢出
注意不要写成for(int i = 2; i <= sqrt(n); i ++),每次执行sqrt(n)都耗费时间*/
if(n % i == 0)//如果可以整除某一个数
return 0;//就说明不是质数
return 1;
}
解答:
#include<bits/stdc++.h>
using namespace std;
bool i(int n){//优化写法
if(n < 2) return 0;//小于2的数不在范围内,直接排除
for(int i = 2; i <= n / i; i ++)//枚举从到n/i
if(n % i == 0)//如果可以整除某一个数
return 0;//就说明不是质数
return 1;
}
int main(){
int n;
cin >> n;
while(n --){
int a;
cin >> a;
if(i(a)) cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}
分解质因数
——例题2 :AcWing 867. 分解质因数
————思路:在 i : 2 ~ (n/i)找到几乎所有的质因数,最后一个质因数就是除剩下的n
质数定理
:n中最多只包含一个大于 sqrt(n)的质因子
枚举:根据刚才的推论sqrt(n) == n / i
只需枚举i:2 ~ (n/i)
;
如果有大于 sqrt(n)的质因子就将其拎出来单独处理,即:
当将所有小于sqrt(n)的质因子除尽后,如果余下的n仍大于1,此时n就是最后一个质因子。
(时间O( log n ~ sprt(n) ))
————总而言之:先在2 ~ (n/i)找到几乎所有的质因数,最后一个质因数就是除剩下的n
解答:
#include<bits/stdc++.h>
using namespace std;
void divide(int n){
for(int i = 2; i <= n / i; i ++)//当i <= n/i 时,说明还没有遍历到重复的约数组
if(n % i == 0 && n){
int s = 0;
while(n % i == 0){
n /= i;
s ++;
}
cout << i <<' '<< s <<endl;
}
if(n > 1) cout << n <<' ' << 1 << endl;
}
int main(){
int n;
cin >> n;
while( n --){
int q;
cin >> q;
divide(q);
cout <<endl;
}
return 0;
}
筛质数
——例题3:AcWing 868. 筛质数
给定一个正整数n,请你求出1~n中质数的个数。
输入格式
共一行,包含整数n。
输出格式
共一行,包含一个整数,表示1~n中质数的个数。
数据范围
1≤n≤106
输入样例:
8
输出样例:
4
————思路:从2~n枚举 i,再从小到大枚举所有已知的质数 primes[j],筛掉合数 i*primes[j],遇到新的质数就入队
——————朴素做法
:
枚举所有小于n的数i
,将i的所有倍数筛掉
。
筛完后剩下的数就是质数
。
朴素做法
void get_primes(int n ){
for(int i = 2; i <= n; i ++){
if(!st[i])
primes[cnt ++] = i;//如果是质数,入队
for(int j = i + i; j <= n; j +=i)
st[j] = 1;//删掉它的所有倍数
}
}
时间分析:n/2 + n/3 +....+n/n = n log n(大概)
——————朴素做法的优化:埃氏筛法``当i不是质数时,没必要筛掉它的倍数
当i不是质数时,没必要筛掉它的倍数
质数定理:1~n当中有 n / logn 个质数
时间是O(n log log n)
约等于O(n)
,和O(n)
一个级别
此算法由一个埃及人发明,所以叫埃氏筛法
注:没被筛掉,说明是质数:因为对于任意数N而言,如果没有被筛掉,就说从2到i-1中没有i的约数,即N是质数。
埃氏筛法
void get_primes(int n ){
for(int i = 2; i <= n; i ++){
if(!st[i]) {
primes[cnt ++] = n;//没被筛掉,说明是质数
for(int j = i + i; j <= n; j += i)//干掉它的所有倍数
st[j] = 1;
}
}
}
时间是O(n log log n)和O(n)一个级别
——————最好的方法----线性筛法求质数
原理
:
核心思路:n只会被n的最小质因子筛掉
.
实现思路:
第一种情况:
i % primes[ j ] == 0
=> primes[ j ] 一定是i的最小质因子.
=> primes[ j ] 一定是primes[ j ] * i的最小质因子.
第二种情况:
i % primes[ j ] != 0
由于是从小到大枚举的质数,且没有枚举到i的任何一个质因子,说明primes[ j ]
一定小于i的最小质因子.
那么primes[ j ] 也一定是primes[ j ] * i的最小质因子.
也就是不管什么情况,if(!st[i]) primes[cnt ++] = i;
这句话是一定成立的.
这样下来,所有小于i的合数都一定会被筛掉
:
证明:对于任意一个合数x,假设primes[j]
是x的最小质因数,i一定会在x之前枚举到x/primes[j]
,这时x就会被筛掉.
举例:比如x=12
时,x的最小质因数primes[j] = 2
,那么i一定会在12之前枚举到x/primes[j] = 6
,此时就会把2*6 = 12 = x
删掉.
时间:数据范围在1e7
以上的时候,线性筛法
比埃氏筛法
快一倍~
void get_primes(int n ){
for(int i = 2; i <= n; i ++){ //从2~n 枚举 i
if(!st[i]) primes[cnt ++] = i; //没被筛掉说明是质数,将这个新的质数加入primes[]里
for(int j = 0; primes[j] <= n / i; j ++){ //从小到大枚举所有已知的质数 primes[j]
/* 当质数大于n / i的时候break;
等价于 primes[j] * i <= n;也就是筛掉所有小于n的合数就可以了
*/
st[primes[j] * i] = 1; //筛掉合数 i*primes[j]
if(i % primes[j] == 0) break;//当这句话发生的时候,就说明primes[j]一定是i的最小质因子,那么用i的最小质因子筛掉i的目的已经达成了,所以跳出循环.
}
}
}
——————总而言之,筛质数最好的办法就是 :从2~n枚举 i,再从小到大枚举所有已知的质数 primes[j],筛掉合数 i*primes[j]
解答3:
#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
int primes[N], cnt;
bool st[N];
void get_primes(int n ){
for(int i = 2; i <= n; i ++){
if(!st[i]) primes[cnt ++] = i;
for(int j = 0; primes[j] <= n / i; j ++){
st[primes[j] * i] = 1;
if(i % primes[j] == 0) break;
}
}
}
int main(){
int n;
cin >> n;
get_primes(n);
cout << cnt << endl;
return 0;
}