背景:
素数(即质数)问题是老问题了,程序员面试问题,大小比赛的热门题。
比如 最简单的判断素数问题,或者计算素数的个数,等等。
简单的判断素数函数:
素数的定义:指在大于1的整数中,只能被1和它本身整除的数。
1既不是素数也不是合数
2是最小的素数
试除法原理:非素数k必定可以被一个小于等于√k的素数整除。
int prime(int n)
{
if(n==1)
return 0;
if(n==2)
return 1;
//函数调用 还有 循环的时间 对付一些入门简单题的
//时间复杂度为√n
for(ll i=2;i*i<=n;i++)
{
if(n%i==0)
//如果有 能被n除的数 ,即n的因子不只是1和本身,不符合素数的概念
//返回0
return 0;
}
return 1;
}
埃氏筛法求素数:
题目:从2到N,每个数判断是否有1和它本身以外的其他因数。
首先将0、1排除:
对于初始队列{2,3,4,5,6,7,8,9,10,11,12,13,14……n},操作步骤如下:
(1)输出最小的素数2,然后筛选掉2的倍数,剩下{3,5,7,9,11,13,……}
(2)输出最小的素数3,然后筛选掉3的倍数,剩下{5,7,11,13,……}
(3)输出最小的素数5,然后筛选掉5的倍数,剩下{7,11,13,……}
继续上面步骤,直到队列为空。
初始化 p = 2,因为2是最小的质数;
枚举所有p的倍数(2p,3p,4p,…),标记为非质数(合数)
找到下一个 没有标记 且 大于p 的数。如果没有,结束运算;如果有,将该值赋予p,重复步骤;
运算结束后,剩下所有未标记的数都是找到的质数。
上动图理解一下:
下面是程序,visit[i]记录i的状态,如果visit[i]=true,表示它被筛选掉了(不是素数)。用prime[]存放素数,例如prime[0]是第一个素数2。
const int maxn=1e7;//定义空间大小,约为10MB
int prime[maxn+1];//存放素数,记录visit[i]=false的项
bool visit[maxn+1];//true表示被筛掉(非素数)
int E_sieve(int n)//埃氏筛法,计算[2,n]内的素数
{
int k=0;//统计素数的个数
for(int i=0;i<=n;i++)
visit[i]=false;//初始化
for(int i=2;i<=n;i++)
{
if(!visit[i])
{
prime[k++]=i;//i是素数,存入prime[]中
for(int j=2*i;j<=n;j+=i)//i的倍数都不是素数
{
visit[j]=true;//标记为true筛掉
}
}
}
return k;
}
这个算法的核心思想是:任何一个p值都是质数,如果存在一个合数,那么就应该存在一个质数能够标记它。
算法的时间复杂度为:O(n/2+n/3+n/5+……)=O(n log log2 n)。
算法的空间复杂度为:当maxn=1e7时约为10MB。一般题目会限制空间为65MB,所以n不能再大了。
当然代码还有两处可以优化。
(1)用来做筛选的数为2,3,5……,最多到√n就可以了。
例如求n=100以内的素数,用2,3,5,7筛选足够了。原理和试除法类似。
(2)for(int j=i*2;j<=n;j+=i)中的j=i*2可以优化为j=i*i。例如i=5时,2*5、3*5、4*5已经在前面i=2,3,4筛选过了。
优化后的代码为:
const int maxn=1e7;//定义空间大小,约为10MB
int prime[maxn+1];//存放素数,记录visit[i]=false的项
bool visit[maxn+1];//true表示被筛掉(非素数)
int E_sieve(int n)//埃氏筛法,计算[2,n]内的素数
{
for(int i=0;i<=n;i++)
visit[i]=false;//初始化
for(int i=2;i*i<=n;i++)//筛选掉非素数
{
if(!visit[i])
{
for(int j=i*i;j<=n;j+=i)//i的倍数都不是素数
{
visit[j]=true;//标记为true筛掉
}
}
}
int k=0;//统计素数的个数
for(int i=2;i<=n;i++)
{
if(!visit[i])
{
prime[k++]=i;//i是素数,存入prime[]中
}
}
return k;
}
看到这里写一题练练手吧:http://onlinejudege.me/problem/1195
可能会卡的进不去,学校的OJ不太行。题面在这里:
超时我已经麻了。。。。
AC代码:
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=5000000;//定义空间大小,约为10MB
int mn[maxn+1];//存放素数,记录visit[i]=false的项
bool visit[maxn+1];//true表示被筛掉(非素数)
int flag=0;
int fun()
{
int k=0;//统计素数的个数
for(int i=0;i<maxn;i++)
visit[i]=false;//初始化
visit[0]=true;
visit[1]=true;
for(int i=2;i*i<=maxn;i++)//筛选掉非素数
{
if(!visit[i])
{
for(int j=i*i;j<=maxn;j+=i)//i的倍数都不是素数
{
visit[j]=true;//标记为true筛掉
}
}
}
for(int i=0;i<maxn;i++)
{
if(!visit[i])
mn[flag++]=i;
}
return flag;
}
int fun1(int *arr , int low , int high , int target)//递归实现二分
{
int middle = (low + high)/2;
if(low > high)
return -1;
if(arr[middle] <=target&&arr[middle+1] >target)
return middle;
if(arr[middle] > target)
return fun1( arr , low , middle - 1 , target);
if(arr[middle] < target)
return fun1( arr , middle + 1 , high , target);
}
int main()
{
std::ios::sync_with_stdio(false);
int n,a,m,flag,flag1,flag2;
flag1=fun();
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&a,&m);
flag2=fun1(mn,0,flag1,a);
printf("%d\n",mn[flag2+m]);
}
return 0;
}
拿去吧,拿去给皇上看,你跟他说,这都是本宫自己抄的,没有假手于人,本宫的手都要断了。
------------哈哈哈哈哈哈