C++ P3383 【模板】线性筛素数

本文介绍了一种高效的线性筛素数算法,并通过示例代码详细展示了其原理及实现过程。该算法能在线性时间内筛选出指定范围内的所有素数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:P3383 【模板】线性筛素数

哎,个人觉得看别人代码总是好的(我也是看书的),人外有人,天外有天嘛!

# include <iostream>
using namespace std;
int n, w, ans[1000010], tot, s; // 1…n 的数中 tot 个素数从小到大放在 ans 数组里(多清楚-_-||)
bool vis[100000010];
int main() {
	cin >> n >> w;
	for(int i = 2; i <= n; i++) {    // 线性筛选素数开始,一共七行,效率高,原理下面解释!
		if(vis[i] == false) ans[++tot] = i;
		for(int j = 1; (j <= tot) && (i*ans[j] <= n); j++) {
			vis[i * ans[j]] = true;
			if(i % ans[j] == 0) break;
		}
	}
	while(w--) {
		cin >> s;
		int i = 1, j = tot, mid;
		while(i <= j) {  // 别问我这是什么(二分查找喽)
			mid = (i + j) / 2;
			if(s == ans[mid]) { cout << "Yes" << endl; break; }
			else if(s < ans[mid]) j = mid - 1;
			else i = mid + 1;
		}
		if(s != ans[mid]) cout << "No" << endl;
	}
	return 0;
}
上面代码中,线性筛选素数的时间复杂度为 O(n), 你没看错。

筛选素数原理:合数可以由一个质数数与另一个数相乘得到 (高级一点的解释我就不会了尴尬

<think>嗯,用户想了解C++98标准下的高效质数筛模板。首先,我得确认用户的需求是什么。质数筛算法有很多种,比如埃拉托斯特尼筛法(埃氏筛)、欧拉筛(线性筛)等等。用户提到的是“高效”,所以可能需要比较不同算法的效率,并给出在C++98下的实现。 首先,我需要回顾一下C++98的限制。C++98不支持auto、范围for循环、移动语义等特性,所以在代码中要避免使用这些。同时,动态数组可能需要用new或者vector来实现。不过,考虑到效率,可能更推荐使用原生数组,或者vector来方便管理内存。 接下来,考虑质数筛的高效实现。埃氏筛的时间复杂度是O(n log log n),而欧拉筛是线性的O(n),但实际应用中埃氏筛可能在小数据情况下更快,因为常数因子较小。不过对于大数,欧拉筛更优。用户可能是在处理大范围的质数筛选,所以需要选择更高效的算法。 埃氏筛的实现相对简单,可以用一个布尔数组标记非质数。例如,从2开始,将每个质数的倍数标记为合数。需要注意的是优化,比如从i*i开始标记,每次增加i。这样可以减少重复标记的次数。但是埃氏筛会重复标记某些合数,比如6会被2和3都标记一次。 欧拉筛则通过每个合数只被其最小质因数筛除,避免了重复,所以效率更高。实现时,需要用额外的数组存储已经找到的质数,并对每个数乘以已知质数来标记合数,当当前数能被已知质数整除时,就停止循环,确保每个合数只被标记一次。 但用户提到的是C++98标准,所以在代码中不能使用C++11及以后的特性。比如,vector的初始化可能需要用循环来设置,或者使用memset等函数。此外,动态数组的大小可能需要预先确定,或者根据输入调整。 另外,内存管理方面,如果处理非常大的n,使用bool数组可能更节省内存,因为bool在C++中通常占用1字节,而bitset或者位操作可以进一步压缩空间,每个位代表一个数是否被标记。不过位操作在代码复杂度上会增加,可能会影响速度,但节省内存。 例如,使用bitset的话,在C++98中是支持的,但大小必须是编译时常量,这限制了灵活性。如果用户需要动态大小,可能需要自己实现位操作,或者使用vector<bool>,虽然vector<bool>在C++中有特化,但可能会有性能问题,因为其存储方式是按位压缩的。 所以,可能的解决方案是,对于埃氏筛,使用vector<bool>或者原生数组,而欧拉筛则需要维护一个质数列表。 现在需要考虑代码示例。比如,埃氏筛的C++98实现: #include <iostream> #include <vector> using namespace std; void eratosthenes(int n) { vector<bool> is_prime(n+1, true); is_prime[0] = is_prime[1] = false; 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]) { cout << i << " "; } } } 这里使用了vector<bool>,在C++98中是允许的。初始化时第二个参数是true,然后逐步标记非质数。需要注意的是,循环的起始点是i*i,这样可以减少重复标记。然后输出所有is_prime为true的索引。 而欧拉筛的实现会更复杂一些: void 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 (size_t j=0; j<primes.size() && i*primes[j] <= n; ++j) { is_prime[i * primes[j]] = false; if (i % primes[j] == 0) { break; } } } // 输出质数 for (size_t i=0; i<primes.size(); ++i) { cout << primes[i] << " "; } } 这里需要注意,内层循环的条件是i*primes[j] <=n,并且当i能被当前质数整除时,就停止,这样可以保证每个合数只被最小的质因数筛掉。这种方法的时间复杂度是线性的,但空间上需要存储质数列表和一个标记数组。 在C++98中,vector的push_back是支持的,所以这段代码应该可以运行。不过,可能需要考虑初始内存分配的问题,预先预留空间可能提高效率,但用户可能更关注正确性。 此外,两种筛法的选择取决于具体的n的大小和内存限制。埃氏筛在n很大时可能会有较多的重复标记,但代码简单,容易实现。欧拉筛更高效,但代码稍复杂,且需要额外的空间存储质数列表。 另外,关于性能优化,比如埃氏筛的分块筛选(分段筛)可以处理更大的n,但可能用户需要的是基本的模板,所以暂时不需要涉及。 还需要考虑输入的n的大小,如果n非常大,比如1e8以上,可能需要考虑内存的使用情况。例如,vector<bool>在存储1e8时需要大约12MB左右的内存(因为每个元素占1位),而欧拉筛的primes数组存储的质数数量大约是n / log n,对于1e8大概是5e6左右,所以内存应该足够。 总结,用户需要的是一个高效的质数筛模板,在C++98下实现。应该提供埃氏筛和欧拉筛两种方法的代码,并说明它们的优缺点,让用户根据实际需求选择。 另外,需要注意代码的正确性,比如埃氏筛的初始化和循环条件,欧拉筛中的break条件是否正确。同时,要提醒用户在使用时注意内存限制,比如当n非常大的时候,可能会因为内存不足而无法运行。 最后,可能需要给出示例代码,并说明如何调用这些函数,以及输出的结果。同时,可以给出一些优化建议,比如在埃氏筛中只处理奇数,或者使用位操作来进一步压缩内存,但这可能会增加代码复杂度。</think>在C++98标准下,质数筛的高效实现主要有两种方法:埃拉托斯特尼筛法(埃氏筛)和欧拉筛(线性筛)。以下是它们的实现模板和特性分析: --- ### **一、埃拉托斯特尼筛法(时间复杂度 $O(n \log \log n)$)** #### 核心思想 通过标记素数的倍数来筛选质数,优化点包括: 1. 从 $i^2$ 开始标记 2. 仅遍历到 $\sqrt{n}$ #### C++98实现代码 ```cpp #include <iostream> #include <vector> using namespace std; void sieve_eratosthenes(int n) { vector<bool> is_prime(n + 1, true); is_prime[0] = is_prime[1] = false; 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]) { cout << i << " "; } } } ``` #### 优点 - 代码简单,易于实现 - 内存占用低(使用位压缩时更优) #### 缺点 - 部分合数会被多次标记(如6会被2和3标记) --- ### **二、欧拉筛(时间复杂度 $O(n)$)** #### 核心思想 确保每个合数仅被其最小质因数筛除,避免重复标记。 #### C++98实现代码 ```cpp #include <iostream> #include <vector> using namespace std; void sieve_euler(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 (size_t j = 0; j < primes.size() && i * primes[j] <= n; ++j) { is_prime[i * primes[j]] = false; if (i % primes[j] == 0) { // 关键:防止重复标记 break; } } } // 输出质数 for (size_t i = 0; i < primes.size(); ++i) { cout << primes[i] << " "; } } ``` #### 优点 - 严格的线性时间复杂度 - 无重复标记操作 #### 缺点 - 需要额外存储质数列表 --- ### **三、性能对比** | 算法 | 时间复杂度 | 适用场景 | |-------------|------------------|------------------------| | 埃氏筛 | $O(n \log \log n)$ | 小规模数据或内存敏感场景 | | 欧拉筛 | $O(n)$ | 大规模数据追求极致效率 | --- ### **四、使用示例** ```cpp int main() { int n = 100; cout << "埃氏筛结果: "; sieve_eratosthenes(n); cout << endl; cout << "欧拉筛结果: "; sieve_euler(n); return 0; } ``` --- ### **五、优化扩展** 1. **内存优化** 用 `vector<bool>` 或位运算压缩标记数组,将内存占用降至 $n/8$ 字节。 2. **分段筛** 处理超大范围时(如 $n \geq 10^8$),分块加载数据到内存。 3. **奇数优化** 除2外,其他偶数无需判断,可减少一半遍历次数。 根据需求选择合适的算法,平衡时间与空间复杂度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值