质数是大家熟知的概念,我们定义一个半质数的概念:如果一个数恰好是两个质数的乘积(可以相同),则称它为半质数。前几个半质数是 4, 6, 9, 10, 14, 15, 21, 22, 25, 26。我们的问题是,输入两个正整数x<=y,问[x,y]之间有多少个半质数?
输入:x,y
输出:[x,y]之间有多少个半质数。
输入数据范围 1<=x<=y<=2000000。
1.我的做法:
需要明白:
(1)两个质数的乘积不等于其他任何两个质数的乘积,因为c=a×b,a,b为质数,那么c的所有因子为1,a,b,a×b,所以c不等于任何其他两个质数的乘积。
也就是说只要两个质数的乘积在[x,y]区间内,那么我们所求的结果就要加1,因为两个质数的乘积不会重复。
(2)某个质数的平方如果大于y,那么这个质数与比他大的质数的乘积肯定大于y。
根据上面两条得出以下做法:
prime保存的是从小到大的质数;
求得y的平方根sy(对下取整),找到小于sy且最接近的质数,赋值给sy,如果不存在这样的质数,返回结果0;
for(sx=2;sx<=sy,sx=下一个质数){
从第i个质数开始,sx与prime[i]乘积大于等于x;
到第j个质数为止,sx与prime[j]乘积小于等于y;
结果+=j-i+1;
}
返回结果
2.我的质数打表方法:
我用两个数组来实现打表:
第一个数组num[2000001]保存的是比本数组下标小且最接近的质数在第二个数组中的下标;
第二个数组prime[]依次保存的是2000000内的质数(从小到大)。
为了便于使用,多分配一个空间,第0个元素不使用,从第一个元素开始。且num[1]=-1,图示如下,打表代码见下面:
这样易于找到比某个数小且最接近的质数,且容易得到这个质数是第几个质数;
如比i小且最接近的质数是prime[num[i]], 这个质数是第num[i]个质数,便于程序计算结果。
当然这里可以用筛法进行优化。
3.C++代码如下:
#include <stdio.h>#include <iostream>#include <string>#include <cmath>#include <ctime>using namespace std;#define MAX 2000000class Test {public: static int* num; static int* prime; static bool isPrime(int i){//判断是否是质数 if(i<2)return false; if(2==i)return true; int s=(int)sqrt(double(i)); for(int j=2;j<=s;++j){ if(0==i%j) return false; } return true; } static int comp(int x,int y){if(y<=3)return 0; int res=0;int sx=2;int sy=prime[num[(int)sqrt((double)y)]]; if(sy<=1)return 0; while(sx<=sy){ int tmpX=x/sx;if(tmpX<=1)tmpX=2;//2跟num数组有关,前两个存储的不是质数数组的索引int indexX=num[tmpX]>num[sx]?num[tmpX]:num[sx];while(sx*prime[indexX]<x)++indexX;int tmpY=y/sx;if(tmpY>=2)//2跟num数组有关,前两个存储的不是质数数组的索引if(num[tmpY]>=indexX)res+=num[tmpY]-indexX+1; sx=prime[num[sx]+1]; } return res; } static int howmany (int x,int y){//第一次调用函数,进行质数打表 static bool firstTime=true; if(firstTime){ firstTime=false;//为了每次取数组元素时,不进行-1操作,多分配一个空间,不使用第0个 num=new int[MAX+1]; prime=new int[150001]; num[1]=-1;//1 num[2]=1;//2 prime[1]=2; int pNum=1; for(int i=3;i<=MAX;++i) if(isPrime(i)){ num[i]=++pNum; prime[pNum]=i; }else num[i]=pNum; } return comp( x,y); }};int*Test::num;int*Test::prime;//start 提示:自动阅卷起始唯一标识,请勿删除或增加。int main(){ clock_t t1=clock(); cout<<Test::howmany(1,2)<<endl; cout<<Test::howmany(1,30)<<endl; cout<<Test::howmany(6,30)<<endl; cout<<Test::howmany(6,2000000)<<endl; cout<<Test::howmany(1,4)<<endl; clock_t t2=clock(); std::cout<<(t2-t1)/(double)CLOCKS_PER_SEC<<std::endl; } //end //提示:自动阅卷结束唯一标识,请勿删除或增加。
4.时间复杂度为O(√y)