求素数的三种方法(详细讲解)!!!!!

目录

暴力算法

埃氏筛法

 欧拉筛法


首先我们解释下什么是素数,素数是除了1和他本身没有其他因子的数,那么根据这一条件我们联想到最简单的暴力算法从1遍历到n/2,看他是否能被n整除如果能就不是素数,直接返回false,否则遍历完所有情况之后返回true。

暴力算法

 下面是代码,可以发现时间复杂度大约为O(n^2),小编测试了一下求100000以内的素数竟然用了8秒.

#include <iostream>
#include <vector>
using namespace std;
#define N 100000
vector<int> vec;
void func()
{
	for(int i=2;i<=N;i++)
	{
		int flag=1;
		for(int j=2;j<=N/2;j++)
		{
			if(i%j==0) flag=0;
		}
		if(flag) vec.push_back(i);
	}
	for(int i=0;i<vec.size();i++)
	cout<<vec[i]<<endl;
}
int main()
{
	func();
 } 

这样的时间复杂度在实战中肯定是不行的啦,所以要优化一下,我们从素数入手,假设我们已将找到了一些素数,我们能否利用这些素数来找到其他的素数或者合数呢,根据素数的条件我们可以知道

1.素数乘以任何数的结果一定是合数

埃氏筛法

  所以我们可以设置一个标记数组将所有数初始化为1,假设他们全部都是素数,然后由于2是素数我们也是从2开始遍历,所以我们可以通过循环和2这个素数找到因子包括2的所有合数并将他们标记为false,以此类推在我们的下一次循环是就是3也就是找到因子包括3的所有合数(实际上就是找到素数的倍数),......这样一来我们就实现了以用得到的素数找到合数,这一套组合拳下来,标记还为1的便是素数了.这也是著名的埃氏筛法求素数.下面是代码.


#include <iostream>
using namespace std;
#define N 1000000
int flag[N];
int num[N];
void sieve()
{
	for(int i=0;i<=N;i++)
	flag[i]=1;
	for(int i=2;i<=N;i++)
	{
		if(flag[i]==1)
		{
			num[++num[0]]=i;
			for(int j=2*i;j<=N;j+=i)
			flag[j]=0; 
		}
	}
	for(int i=1;i<=num[0];i++)
	cout<<num[i]<<endl;
}
int main()
{
   sieve();	
} 
 

 欧拉筛法

  而利用埃氏筛法求素数的时间复杂度是O(nlogn),这样的时间复杂度会不会还是太长了呢因为第二个循环是采用加法的形式当数据过大时,比较小的素数的循环长度还是比较大,或者说我们能不能一次循环标记多个合数呢,这里我们又想到可以将两层循环结合起来筛数,因为任何数乘以素数都是合数,所以我们第二层循环的上限改为我们已经得到的素数个数,然后将每个素数乘以i,得到的结果为合数标记,这样是不是就减少了第二层循环的次数,而当i和素数过大时他们的乘积又会大于n所以还要加上乘积小于n的条件, 我们思考一个问题,当i为一个合数时它与素数相乘后会不会在后序循环被重复标记,比如当i为4,素数为3时 和当i为6和素数为2时,12这个数就会被重复标记,所以当i为合数时它应该只能被他的最小素因子筛去,所以要跳出循环避免重复的标记.这就是欧拉筛法的核心过程下面是代码.

#include <iostream>
#include <string.h>
#include <vector>
using namespace std;
#define N 100000   //求1到100000之间的素数
int status[N];   //数据 初始为1假设每个数都是素数
vector<int> prime;
void Euler()
{
    vector<int> prime;
    status[0] = status[1] = 0; //0和1不是素数
    for (int i = 2; i < N; i++)
    {
        if (status[i])
        prime.push_back(i);
        for (int j = 0; j < prime.size() && prime[j] * i <= N; j++)
        {
            status[prime[j] * i] = 0;  //两个数的乘积不是素数;
            if (i % prime[j] == 0)
                break;  //这里break是因为防止重复标记,比如2*6=12,3*4=12;所以当i为合数时只需要乘以第一个素因子即可 
        }
    }
    for (int i = 0; i < prime.size(); i++)
        cout << prime[i] << endl;
}

int main()
{
    memset(status, 1, sizeof(status));
    Euler();
}

我们看一下当数据为100000时它所花费的时间为2秒左右

看到这里还不点赞收藏一下吗,过段时间说不定就会忘了哦!

这里留了一道题给大家可以用到上面所学的知识

P1217 [USACO1.5] 回文质数 Prime Palindromes - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

感谢收看!!!!

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值