欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

信息学奥赛一本通 2040:【例5.7】筛选法找质数 (普通筛 线性筛)

发布时间:2025/3/17 编程问答 52 豆豆
生活随笔 收集整理的这篇文章主要介绍了 信息学奥赛一本通 2040:【例5.7】筛选法找质数 (普通筛 线性筛) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

【题目链接】

ybt 2040:【例5.7】筛选法找质数

【题目考点】

1. 普通筛法找质数(埃拉托色尼筛法)

基本思想:把从2到N的一组正整数从小到大按顺序排列。从中依次删除2的倍数、3的倍数、5的倍数,直到N\sqrt{N}N的倍数为止,剩余的即为2~N之间的所有质数。
普通筛法复杂度为:O(nloglogn)O(nloglogn)O(nloglogn)

为什么直到N\sqrt{N}N的倍数为止,以下是证明:

  • 命题:对于整数n满足N<n≤N\sqrt{N} < n \le NN<nN,n是质数,或n是合数时,存在一个整数a满足a≤Na \le \sqrt{N}aN,a是n的因数。
  • 证明:显然n可以是质数。如果n是合数,使用反证法。
  • 假设n是合数时,n的因数(除了1)都大于N\sqrt{N}N。由于n是合数,n至少有两个不为1或n的因数,设为a,b,且存在等式a⋅b⋅x=na\cdot b\cdot x = nabx=n,其中x是某个正整数。
    a>N,b>N⇒a⋅b>N≥na>\sqrt{N}, b>\sqrt{N} \Rightarrow a\cdot b >N\ge na>N,b>Nab>Nn,即a⋅b>na\cdot b > nab>n
    根据a⋅b⋅x=na\cdot b\cdot x = nabx=n,有a⋅b≤na\cdot b \le nabn
    出现矛盾,因而假设不成立,原命题得证。

2. 线性筛(欧拉筛)

基本思想:让每个合数只通过它最小的质因数被筛掉。

例:
8的最小质因数是2,那么让合数8通过质数2被筛掉。
15的最小质因数是3,那么让合数15通过质数3被筛掉。

普通筛法中,数字可能会被多次筛掉,线性筛中,每个数字只被筛掉一次。
线性筛法复杂度为:O(n)O(n)O(n)
线性筛的字面意思就是该算法的复杂度为线性的。

例:对于合数15
在普通筛法中:看质数3的倍数时,15被筛掉一次,看质数5的倍数时,15被筛掉一次。
在线性筛中: 15只会通过质数3被筛掉一次。

线性筛法中,维护一个质数数组prime[]。
算法如下:

  • 每次循环观察一个数字i
  • 如果i是质数,加入质数数组
  • 无论i是不是质数,都顺序遍历质数数组,取到的质数为prime[j]
    • 只要i不是prime[j]的倍数,那么prime[j]就是i*prime[j]的最小质因数。i*prime[j]这个合数通过prime[j]这个最小质因数被筛掉了。
    • 如果i是prime[j]的倍数,则跳出循环

原理解释如下:

  • 由于prime数组中的质数是递增的,在遍历prime数组的过程中,只要没有遇到i是prime[j]的倍数的情况,那么prime[j]一定是i*prime[j]的最小质因数。当第一次遇到i是prime[j]的倍数的情况时,prime[j]仍然是i*prime[j]的最小质因数。
  • 如果继续看prime[j]后面第x个质数(x大于等于1),此时要判断数字Y=i*prime[j+x]是否要被筛掉。由于prime[j+x] > prime[j],i已经具有更小的质因数prime[j],所以数字Y的最小质因数是prime[j]而不是prime[j+x],所以Y这个数不应该在i为这个值时被筛掉,它应该在i为Y/prime[j]时被筛掉。

例:假设i是25。25不是2的倍数,那么2是2*25的最小质因数,25也不是3的倍数,那么3是3*25的最小质因数,25是5的倍数,5仍然是5*25的最小质因数。
25不是7的倍数,7不是7*25的最小质因数了。7*25=175被筛掉的时机是:当i循环到35时,175=35*5,5是175的最小质因数,175通过质数5被筛掉。

【题解代码】

解法1:普通筛法(埃拉托色尼筛法)

#include<bits/stdc++.h> using namespace std; int main() {bool isPrime[1005] = {};//isPrime[i]:i是否是质数 int n;cin >> n;for(int i = 2; i <= n; ++i)//初值状态下,把每个数字都标记为质数 isPrime[i] = true;//isPrime[0],与isPrime[1]都为false,0,1都不是质数 for(int i = 2; i <= int(sqrt(n)); ++i){if(isPrime[i]){for(int j = 2*i; j <= n; j += i)//如果一个数字是某质数的倍数,那么把它标记为合数 isPrime[j] = false;}}for(int i = 2; i <= n; ++i){if(isPrime[i])cout << i << endl;}return 0; }

解法2:线性筛(欧拉筛)

#include<bits/stdc++.h> using namespace std; int main() {bool isPrime[1005] = {};//isPrime[i]:i是否是质数 int n, prime[1005], psize = 0;//prime:质数数组 psize:prime数组长度 cin >> n;for(int i = 2; i <= n; ++i)//初值状态下,把每个数字都标记为质数 isPrime[i] = true;//isPrime[0],与isPrime[1]都为false,0,1都不是质数 for(int i = 2; i <= n; ++i){if(isPrime[i])//如果i是质数 prime[++psize] = i;//将i填充到指数数组prime for(int j = 1; j <= psize && i*prime[j] <= n; ++j)//遍历质数数组 {//筛去当前观察的数i与某质数prime[j]的乘积isPrime[i*prime[j]] = false;//i*prime[j]这个数通过prime[j]筛掉 if(i%prime[j] == 0)break;}}for(int i = 2; i <= n; ++i){if(isPrime[i])cout << i << endl;}return 0; }

总结

以上是生活随笔为你收集整理的信息学奥赛一本通 2040:【例5.7】筛选法找质数 (普通筛 线性筛)的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。