C++算法——埃氏筛

C++判断素数:埃氏筛

思路

这个算法是利用打表的方法来计算的:
首先,我们要知道一个特性
就是一个质数的倍数,一定是一个合数

利用这个特性
我们可以写出以下代码

for (int i = 2; i * i <= n; i++)
{
	if (!prime[i] == true)
	{
		for (int j = i + i; j <= n; j++)
		{
			prime[j] = true;//这个数就是质数的倍数
		}
	}
}

这段代码就是埃氏筛的核心代码(就这些)
上面的判断是为了以防重复打表,下面的for是为了标记这个质数所有小于n的合数

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1E9 + 7;

bool prime_n[MAXN];

int EhrlichSieve(bool prime[], int n)
{
	for (int i = 2; i * i <= n; i++)
	{
		if (!prime[i] == true)
		{
			for (int j = i + i; j <= n; j++)
			{
				prime[j] = true;
			}
		}
	}
	return 0;
}
int main()
{
	EhrlichSieve(prime_n, 1000000);
	int q;
	cin >> q;
	while (q--)
	{
		int x;
		cin >> x;
		if (prime_n[x] == false)
		{
			cout << x << "是素数\n";
		}
		else
		{
			cout << x << "不是素数\n";
		}
	}
	return 0;
}
//这里的"EhrlichSieve"使用的是百度机翻,不知道对不对
欧拉筛法(线性筛)和埃氏筛法(埃拉托斯特尼筛法)都是用于**高效筛选出小于等于某个整数 n 的所有素数**的算法。它们在时间复杂度、实现方式和适用场景上有显著区别。 下面我将详细解释两者的原理、C++ 实现,并对比其优劣。 --- ## ✅ 1. 埃拉托斯特尼筛法(Sieve of Eratosthenes,简称“埃氏筛”) ### 🔍 原理: - 从最小的质数 2 开始,把它的所有倍数(除了自己)标记为合数。 - 然后找下一个未被标记的数,它一定是质数,再将其所有倍数标记为合数。 - 重复此过程直到遍历到 √n。 > ❗ 注意:一个合数可能被多个质因数重复标记(如 12 被 2 和 3 都标记过),这是效率损失的原因。 ### ⏱ 时间复杂度:O(n log log n) ### ✅ 空间复杂度:O(n) ### 📌 C++ 实现: ```cpp #include <iostream> #include <vector> using namespace std; vector<int> sieve_of_eratosthenes(int n) { vector<bool> is_prime(n + 1, true); vector<int> primes; is_prime[0] = is_prime[1] = false; for (int i = 2; i * i <= n; i++) { if (is_prime[i]) { // 标记 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 = 30; vector<int> primes = sieve_of_eratosthenes(n); cout << "Primes up to " << n << ": "; for (int p : primes) { cout << p << " "; } cout << endl; return 0; } ``` --- ## ✅ 2. 欧拉筛法(Euler's Sieve,又称线性筛) ### 🔍 原理: - 维护一个 `primes` 数组记录已知质数。 - 对每个数 i(从 2 到 n),用已知质数去筛掉 `i × primes[j]`。 - **关键优化**:一旦 `primes[j]` 整除 `i`,就停止后续筛除 —— 这保证了每个合数只被它的**最小质因数**筛一次。 > ✅ 因此,每个合数仅被标记一次 → 时间复杂度 O(n) ### ⏱ 时间复杂度:O(n)(线性) ### ✅ 空间复杂度:O(n) ### 📌 C++ 实现: ```cpp #include <iostream> #include <vector> using namespace std; vector<int> euler_sieve(int n) { vector<bool> is_prime(n + 1, true); vector<int> primes; is_prime[0] = is_prime[1] = false; for (int i = 2; i <= n; i++) { if (is_prime[i]) { primes.push_back(i); } // 用已知质数去筛合数 for (int j = 0; j < primes.size() && i * primes[j] <= n; j++) { is_prime[i * primes[j]] = false; if (i % primes[j] == 0) { break; // 关键:primes[j] 是 i 的最小质因数 } } } return primes; } int main() { int n = 30; vector<int> primes = euler_sieve(n); cout << "Primes up to " << n << ": "; for (int p : primes) { cout << p << " "; } cout << endl; return 0; } ``` --- ## 🔁 两种筛法的核心区别 | 特性 | 埃氏筛 | 欧拉筛 | |------|--------|--------| | 时间复杂度 | O(n log log n) | O(n)(线性) | | 是否重复标记 | 是(如 12 被 2 和 3 都筛) | 否(每个合数只被最小质因数筛) | | 实现难度 | 简单直观 | 稍复杂,需理解 `i % primes[j] == 0` 的意义 | | 筛的过程中是否收集质数 | 可以 | 必须收集 | | 适合学习阶段 | 初学者 | 中级/进阶 | | 扩展性(如求最小质因数、φ 函数等) | 差 | 强 | --- ## 💡 为什么 `i % primes[j] == 0` 就要 break? 假设 `i` 能被 `primes[j]` 整除,说明 `primes[j]` 是 `i` 的一个质因数。 那么对于下一个质数 `primes[j+1] > primes[j]`,考虑 `i * primes[j+1]`: - 它的最小质因数其实是 `primes[j]`(因为 `i` 包含 `primes[j]`) - 所以应该由 `primes[j]` 来筛掉它,而不是现在就筛 👉 提前 break 可避免重复筛,确保线性复杂度。 --- ## ✅ 示例输出(n=30): ``` Primes up to 30: 2 3 5 7 11 13 17 19 23 29 ``` 两者输出一致,但欧拉筛更快更高效。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值