文章目录
- 质数的判定:试除法
- 分解质因数:试除法
- 筛质数
- 朴素做法
- 优化:埃氏筛法
- 优化:线性筛法
质数的判定:试除法
不推荐
i*i<=n
而推荐i<=n/i
的原因
可能存在i*i
不溢出,但是(i+1)(i+1)
溢出的情况
#include <iostream>
using namespace std;int n;bool isPrime(int n)
{if(n == 1) return false;for(int i=2; i<=n/i; i++) {if(n%i == 0) {return false;}}return true;
}int main()
{cin >> n;for(int i=0; i<n; i++) {int a;cin >> a;if(isPrime(a)) cout << "Yes" << endl;else cout << "No" << endl;}return 0;
}
时间复杂度: O ( n ) O(\sqrt{n}) O(n)
分解质因数:试除法
- 分解质因数过程中,最多只含一个大于 n \sqrt{n} n 的质因子
#include <iostream>
using namespace std;void divide(int n)
{for(int i=2; i<=n/i; i++) {if(n%i == 0) {int cnt = 0;while(n%i == 0) {cnt++;n /= i;}cout << i << " " << cnt << endl;}}if(n > 1) cout << n << " " << 1 << endl;cout << endl;
}int main()
{int n;cin >> n;for(int i=0; i<n; i++) {int a;cin >> a;divide(a);}return 0;
}
时间复杂度:
- 最坏: O ( n ) O(\sqrt{n}) O(n)
- 最好【n 是2的倍数】: O ( l g n ) O(lgn) O(lgn)
筛质数
朴素做法
#include <iostream>
using namespace std;const int N = 1000100;
int primes[N];
bool st[N];
int cnt = 0;void get_primes(int n)
{for(int i=2; i<=n; i++) {if(!st[i]) {primes[cnt++] = i; // 将素数存起来}// 不管是合数还是质数,都用来筛掉后面它的倍数for(int j=i; j<=n; j+=i) {st[j] = true;}}
}int main()
{int n;cin >> n;get_primes(n);cout << cnt << endl;return 0;
}
时间复杂度:近似 O ( n l g n ) O(nlgn) O(nlgn)
- T ( n ) = n 2 + n 3 + n 4 + . . . + 1 = n ∗ ( 1 2 + 1 3 + 1 4 + . . . + 1 n ) T(n) = \frac{n}{2}+\frac{n}{3}+\frac{n}{4}+...+1=n*(\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+...+\frac{1}{n}) T(n)=2n+3n+4n+...+1=n∗(21+31+41+...+n1)
优化:埃氏筛法
- 质数定理:
1~n
中有 n l n n \frac{n}{ln{n}} lnnn 个质数
#include <iostream>
using namespace std;const int N = 1000100;
int primes[N];
bool st[N];
int cnt = 0;void get_primes(int n)
{for(int i=2; i<=n; i++) {if(!st[i]) {primes[cnt++] = i; // 将素数存起来for(int j=i; j<=n; j+=i) {st[j] = true; // 可以用质数就把所有的合数都筛掉}}}
}int main()
{int n;cin >> n;get_primes(n);cout << cnt << endl;return 0;
}
时间复杂度: O ( n l g l g n ) O(nlglgn) O(nlglgn)
优化:线性筛法
- 每个合数的最小质因子是唯一的。
- 合数 t 最小因子为 p,最大因数为 i,则有
t = p*i
,且 p 一定是质数【若 p 不为质数,可继续拆分出最小的质数,剩余部分可以合并到 i 里去】 - 按照最小质因子筛选,可以保证每个数都只会被筛一遍
- 虽然
primes[]
看上去还是那个primes[]
,但是i
再也回不到过去,所以他们结合出来的东西也不会一样。
#include <iostream>
using namespace std;const int N = 1000100;
int primes[N];
bool st[N];
int cnt = 0;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++) {// 用最小质因子(对应primes数组)去筛合数// 确保会出现2*3但不会重复计算3*2st[primes[j]*i] = true;// 此时,primes[j]一定是i的最小质因子,无需继续遍历// primes要小与等于i的最小质因子// 这样能保证每个数遍历一遍,而没有重复if(i%primes[j] == 0) break;// 如果这里不break的话,primes[j]已经是最小质因子,primes[j+1] > primes[j],一定不可能是最小质因子}}
}int main()
{int n;cin >> n;get_primes(n);cout << cnt << endl;return 0;
}