素数专题(未完待续)
素数
一、素数与合数的定义
设整数 \(p\ne0\),\(±1\). 如果它除了显然约数 \(±1\), \(±p\) 外没有其它约数, 那么 \(p\) 就称为不可约数,也叫素数. 若 \(a\ne0\),\(±1\) 且 \(a\) 不是不可约数,则称 \(a\) 为合数. (没有特殊说明, 素数总是指正的)
定理 设整数 \(a\geqslant2\), 那么 \(a\) 一定可以表示为素数的乘积(包括 \(a\) 本身是不可约数),即
\[a = p_{1}p_{2}···p_{s},\]
其中 \(p_{j}(1\leqslant j\leqslant s)\) 是不可约数.
二、有关素数的重要推论 (设整数 \(a\geqslant2\))
(i) 若 \(a\) 是合数, 则必有不可约数 \(p\nmid a\), \(p\leqslant \sqrt{a}\) ;
(ii) 若 \(a\) 有定理中的表示式,则必有不可约数 \(p\nmid a\), \(p\leqslant a^{1/s}\) .
三、有关素数的算法
1.素性测试 \(O(n\sqrt{n})\)
//假设输入的数为正数
bool is_prime(int n) {
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return n > 1; //特判1
}
2.埃拉托斯特尼筛法 \(O(nloglogn)\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 10000;
//埃拉托斯特尼筛法(sieve of Eratosthenes), 复杂度 O(nloglogn)
int prime[N], cnt = 0; //prime数组用于存放素数, cnt用于统计素数的个数
bool is_prime[N]; //is_prime数组用于标记当前数是否被筛选过
void sieve_of_Eratosthenes(int n) {
memset(is_prime, 1, sizeof is_prime); //初始化
is_prime[0] = is_prime[1] = false; //将 0 和 1 标记为已筛选
for (int i = 2; i <= n / i; i++) //i循环到sqrt(n)足矣
if (is_prime[i]) {
prime[cnt++] = i;
for (int j = i * i; j <= n; j += i) //因为2,3,4,...,i-1的倍数之前已经筛过了,直接从i的倍数开始筛
is_prime[j] = false; //只要是i的倍数均标记为非素数
}
return;
}
3.欧拉筛法(Sieve of Euler) 几乎是\(O(n)\)
//欧拉筛法(sieve of Euler), 复杂度 几乎是O(n)
int primes[N], cnts = 0; //primes数组用于存放素数, cnts用于统计素数的个数
bool is_notprime[N] = {1, 1}; //首先将 0 和 1 初始化为非素数
void sieve_of_Euler(int n) {
for (int i = 2; i < n; i++) {
if (!is_notprime[i]) primes[cnts++] = i;
for (int j = 0; j < cnts && i * primes[j] < n; j++) {
is_notprime[i * primes[j]] = 1;
if (!(i % primes[j])) break; //确保每个合数只会被它的最小质因子筛除,并且只筛除一次
}
}
}