题目如下:
链接:筛选法求素数__牛客网
来源:牛客网
用筛选法求n以内的素数。筛选法求解过程为:将2~n之间的正整数放在数组内存储,将数组中2之后的所有能被2整除的数清0,再将3之后的所有能被3整除的数清0 ,以此类推,直到n为止。数组中不为0 的数即为素数。
一开始没看明白题目意思,以为只需要满足不是2或者3的倍数就可以了,然后发现出错了,后来才发现搞错掉了
然后我就查了下判断素数的方法,有如下几个,但不止这几个:1.AKS算法——公认最优的算法(但实际应用中不常见) 2.Miller—Rabbin算法(基于费马小定理的扩展) 3.试除法(优化6倍法) 4.筛选法
1.AKS算法
直说吧,没看懂,太复杂,我也不会,但从GPT那里获得了c++的简易模板
代码如下:
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
// 求最大公约数的辗转相除法
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
// 快速幂算法
ll binpow(ll a, ll b, ll m) {
ll res = 1;
a %= m;
while (b > 0) {
if (b & 1) {
res = res * a % m;
}
a = a * a % m;
b >>= 1;
}
return res;
}
// 判断一个数是否为素数
bool is_prime(ll n) {
if (n <= 1) {
return false;
}
if (n <= 3) {
return true;
}
if (n % 2 == 0 || n % 3 == 0) {
return false;
}
ll r = sqrt(n);
for (ll i = 5; i <= r; i += 6) {
if (n % i == 0 || n % (i + 2) == 0) {
return false;
}
}
return true;
}
// 判断n是否是a的幂
bool is_power(ll n) {
if (n <= 1) {
return true;
}
ll r = sqrt(n);
for (ll a = 2; a <= r; ++a) {
ll p = a;
while (p <= n) {
p *= a;
if (p == n) {
return true;
}
}
}
return false;
}
// AKS算法判断n是否为素数
bool AKS(ll n) {
// 如果n是a的幂,则n不是素数
if (is_power(n)) {
return false;
}
// 寻找r,满足n^r-1 = (n-1)q,其中q为素数
ll r = 2;
while (r <= sqrt(n)) {
if (n % r == 0) {
return false;
}
ll phi_r = r - 1;
if (gcd(n, r) == 1) {
bool is_valid = true;
for (ll a = 2; a <= sqrt(phi_r) && is_valid; ++a) {
if (gcd(a, r) == 1) {
is_valid = binpow(a, phi_r, n) != 1;
}
}
if (is_valid) {
ll q = (n - 1) / r;
if (gcd(q, r) == 1 && binpow(n, q, r) == 1) {
return true;
}
}
}
++r;
}
return false;
}
int main() {
ll n;
cin >> n;
if (AKS(n)) {
cout << n << " is prime" << endl;
} else {
cout << n << " is not prime" << endl;
}
return 0;
}
2.Miller—Rabbin算法
原理:
1如果要判断的数n等于2或3,则判定为素数。
2如果n可以以表示为a^b的形式,则判定n为合数。
3如果n不是奇数,则判定n为合数。
4计算r使得r是最小的满足下列不等式之一的整数。
5对所有满足1 < a ≤ r的整数a,判断是否满足条件之一:
(i) a^n ≡ a mod n (ii) 对于所有0 < k < n,有(a^k) mod (x^r - 1, n) = 1
如果满足上述条件,则判定n为素数,否则n为合数。
3.试除法(优化6倍法)
bool is_prime(int n) {
if (n <= 1) return false; // 1 不是素数
if (n <= 3) return true; // 2 和 3 是素数
if (n % 2 == 0 || n % 3 == 0) return false; // 排除掉 2 和 3 的倍数
int max_divisor = sqrt(n); //求待判断数开方数
for (int i = 5; i <= max_divisor; i += 6) {
if (n % i == 0 || n % (i + 2) == 0) {
return false;
}
}
return true;
}
试除法(也称质因数分解法)是一种用来判断一个数是否为素数的算法。它的基本原理是将要判断的数 n 依次除以 2 到 sqrt{n} 之间的每个整数,如果都不能整除,则 n 是素数。如果存在能整除 n 的整数,则 n 不是素数。
在优化的试除法中,我们可以先判断 n 是否为 2 或 3,然后只对 6k±1 的形式进行试除,这样能够减少试除的次数,提高算法效率。具体实现中,我们只需要判断 6k±1 ≤ √n 是否成立即可。
需要注意的是,如果 n 为合数,它必定存在小于 sqrt{n} 的质因子,因此只需要试除 2 到 sqrt{n} 之间的数即可,不需要试除所有的数
4.筛选法
#include <iostream>
#include <vector>
using namespace std;
vector<int> get_primes(int n) {
vector<int> primes;
vector<bool> is_prime(n + 1, true); // 初始化所有数为素数
is_prime[0] = is_prime[1] = false; // 排除0和1不是素数的情况
// 从2开始筛选,将所有的倍数标记为非素数
for (int i = 2; i * i <= n; i++) {
if (is_prime[i]) {
for (int j = i * i; j <= n; j += i) {
is_prime[j] = false;
}
}
}
// 收集所有的素数
for (int i = 2; i <= n; i++) {
if (is_prime[i]) {
primes.push_back(i);
}
}
return primes;
}
int main() {
int n;
cin >> n;
vector<int> primes = get_primes(n);
for (int prime : primes) {
cout << prime << " ";
}
cout << endl;
return 0;
}
它的基本思想是先假设所有数都是素数,然后从2开始,将其倍数标记为合数,最终剩下的未被标记的数即为素数。