【阿里】质数【欧拉筛法,埃氏筛法和开根】
题目描述:
输入 n n n 个不大于 1 0 5 10^5 105 的正整数。要求全部储存在数组中,去除掉不是质数的数字,依次输出剩余的质数。
输入格式
第一行输入一个正整数 n n n,表示整数个数。
第二行输入 n n n 个正整数 a i a_i ai,以空格隔开。
输出格式
输出一行,依次输出 a i a_i ai 中剩余的质数,以空格隔开。
样例 #1
样例输入 #1
5
3 4 5 6 7
样例输出 #1
3 5 7
提示
数据保证, 1 ≤ n ≤ 100 1\le n\le100 1≤n≤100, 1 ≤ a i ≤ 1 0 5 1 \leq a_i \leq 10^5 1≤ai≤105。
OJ链接:
https://www.luogu.com.cn/problem/P5736
解题思路一:开根:最简单的。注意 for i in range(2, math.isqrt(num) + 1):,range里面只能用整数平方根isqrt()
#include <bits/stdc++.h>
using namespace std;
long long shai(long long a)
{
if(a<2)
{
return 0;//小于2的0和1绝对不是,我被坑了2次。
}
for(int i=2;i<=sqrt(a);i++)
{
if(a%i==0)
{
return 0;//传进来的数已经有了除1和本身之外的数了,就绝对不是素数。
}
}
return 1;//跑遍了循环没找到除1和本身之外的数,那就是素数,返回1。
}
int main()
{
long long n;
cin>>n;
long long qwq[100001];
for(int i=1;i<=n;i++)
{
cin>>qwq[i];//输入数。
}
for(int i=1;i<=n;i++)
{
if(shai(qwq[i])==1) cout<<qwq[i]<<" ";//如果函数结果为1就输出这个数。
}
return 0;
}
# python
import math
n = int(input())
nums = list(map(int, input().split()))
def shai(num):
if num < 2:
return 0
for i in range(2, math.isqrt(num) + 1):
if num % i == 0:
return 0
return 1
for num in nums:
if shai(num):
print(num, end = " ")
时间复杂度:O(nsqrt(n))
空间复杂度:O(1)
解题思路二:欧拉筛法(最优解)
#include <bits/stdc++.h>
using namespace std;
long long n,m;
bool vis[10000001]={1,1};//0,1均既不是素数,也不是和数,所以先标记为不是
int Prime[10000001],k;
void prime(long long n)
{
for(int i=2;i<=n;i++)//最小的素数是2
{
if(!vis[i])
{
Prime[++k]=i;//如果是素数就标记一下
}
for(int j=1;j<=k;j++)//j小于当前所有的素数的个数
{
if(Prime[j]*i>n)
{
break;
}
vis[Prime[j]*i]=true;//用素数依次×i,结果标记为合数
if(i%Prime[j]==0)
{
break;
}
}
}//欧拉筛法,就是拿当前的数×之前的筛出来的素数,这个数即为合数
}
int main()
{
cin>>n;
prime(100001);//在10的5次方范围内筛素数
for(int i=1;i<=n;i++)
{
int t;
cin>>t;
if(!vis[t])//上面标记过了,这时输入后直接判断就行了
{
cout<<t<<" ";
}
}
return 0;
}
# python
N = 10 ** 7 + 2
vis = [0] * N # 标记是质数或是合数
vis[0] = vis[1] = 1 # 0, 1既不是素数,也不是和数,所以先标记为不是
Prime = [0] * N # 仅仅标记当前遇到的素数
k = 1
n = int(input())
nums = list(map(int, input().split()))
def prime(x): #欧拉筛法,就是拿当前的数×之前的筛出来的素数,这个数即为合数
for i in range(2, n+1): # 最小的素数是2
if not vis[i]:
global k
Prime[k] = i # 如果是素数就标记一下
k += 1
for j in range(1, k+1): #j小于当前所有的素数的个数
if Prime[j] * i > x:
break
vis[Prime[j]*i] = 1 #用素数依次×i,结果标记为合数
if i % Prime[j]==0:
break
prime(100001) # 数据范围
for num in nums:
if not vis[num]:
print(num, end = " ")
时间复杂度:O(n)
空间复杂度:O(n)
解题思路三:【埃氏筛法】原理就是开一个数据范围的数组,首先将所有数标记为非质数。然后依次遍历for(int i=2;i<=qwq;i++),内层循环将for j in range(i + i, num + 1, i):【i+i, i+i+i,i+i+i+i, …】标记为质数。最终可以得到这个范围内的所有质数与合数。
埃氏筛法(比欧拉筛法更容易理解)埃氏筛法用的较少,但是它的思路可以用于其他一些题目,思路比较重要。
#include <bits/stdc++.h>
using namespace std;
bool vis[100002]={1,1};//0,1标为不是
int n;
void Era(int qwq)
{
for(int i=2;i<=qwq;i++)
{
if(vis[i])
{
continue;
}//是合数就不执行
for(int j=i*2;j<=qwq;j+=i)//从i×2开始筛,因为进过判断后i为素数
{
vis[j]=true;//j=i的倍数,每次加i,即为i的倍数每次加1,p数组的第j个元素标为合数
}
}
}
int main()
{
cin>>n;
int tmp;
Era(100001);
for(int i=1;i<=n;i++)
{
scanf("%d",&tmp);
if(!vis[tmp])//已经记下了,判断一下即可
{
cout<<tmp<<" ";
}//真就不是,假就是
}
// 打印vis数组内容
// for(int i = 0; i <= 100; i++) {
// cout << "vis[" << i << "] = " << vis[i] << endl;
// }
return 0;
}
# python
n = int(input())
nums = list(map(int, input().split()))
vis = [0] * 100002
vis[0], vis[1] = 1, 1 # 0,1标为不是质数
def Era(num):
for i in range(2, num + 1):
if vis[i]:
continue
for j in range(i + i, num + 1, i):
vis[j] = 1
Era(100001)
for num in nums:
if not vis[num]:
print(num, end = " ")
时间复杂度:O(n)
空间复杂度:O(n)