质数与合数

1|0质数与合数

1|1判断质数

显然,每个合数都会有相对较小的质因子。

a 为合数,则 a=pq(p,q>1)。易证 pq 中一定有一个不超过 a(若两个都超过 a,则 pq>a)。

更严格地,若 a 为合数,则一定存在质数 p|a,且 pa

bool is_prime(int x) { for (int i = 2; i * i <= x; i++) if (x % i == 0) return false; return true; }

1|0埃拉托斯特尼筛法

考虑这样一件事情:对于任意一个大于 1 的正整数 n ,那么它的 x 倍就是合数x>1。利用这个结论,我们可以避免很多次不必要的检测。

如果我们从小到大考虑每个数,然后同时把当前这个数的所有(比自己大的)倍数记为合数,那么运行结束的时候没有被标记的数就是素数了。

vector<int> prime; bool no_prime[N]; void Eratosthenes(int n) { no_prime[0] = no_prime[1] = true; for (int i = 2; i <= n; i++) { if (!no_prime[i]) { for (int j = i << 1; j <= n; j += i) { no_prime[i] = true; } } } }

1|0线性筛法

埃氏筛法仍有优化空间,它会将一个合数重复多次标记。有没有什么办法省掉无意义的步骤呢?答案是肯定的。

如果能让每个合数都只被标记一次,那么时间复杂度就可以降到 O(n) 了。

vector<int> pri; bool not_prime[N]; void pre(int n) { for (int i = 2; i <= n; ++i) { if (!not_prime[i]) { pri.push_back(i); } for (int pri_j : pri) { if (i * pri_j > n) break; not_prime[i * pri_j] = true; if (i % pri_j == 0) { // i % pri_j == 0 // 换言之,i 之前被 pri_j 筛过了 // 由于 pri 里面质数是从小到大的,所以 i 乘上其他的质数的结果一定会被pri_j 的倍数筛掉,就不需要在这里先筛一次,所以这里直接 break掉就好了 break; } } } }

1|2例题

P1835 素数密度

首先结合上面推论,理论上只要利用不超过 n 的质数就可以筛除 n 范围内的所有合数。

对于题目区间 2147483647,先预先求出 2147483647=50000 以内所有质数,再用这些质数对 [L,R] 上的合数筛除即可。

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> const int N = 1e6 + 10; using ll = long long; bool is_prime[N]; bool no_prime[N]; int pri[10000]; int init() { int cnt = 0; is_prime[0] = is_prime[1] = false; for (int i = 2; i <= 50000; ++i) is_prime[i] = true; for (int i = 2; i * i <= 50000; i++) { if (is_prime[i]) { for (int j = i * i; j <= 50000; j += i) { is_prime[j] = false; } } } for (int i = 2; i <= 50000; i++) { if (is_prime[i]) pri[cnt++] = i; } return cnt; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); int cnt = init(); ll L, R; std::cin >> L >> R; for (int i = 0; i < cnt; i++) { for (ll j = std::max(2ll, (L - 1) / pri[i] + 1) * pri[i]; j <= R; j += pri[i]) { no_prime[j - L] = true; } } int ans = 0; no_prime[1] = true; for (ll i = L; i <= R; i++) { if (!no_prime[i - L]) ans++; } std::cout << ans; return 0; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17873946.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示