质数是大家熟知的概念,我们定义一个半质数的概念:如果一个数恰好是两个质数的乘积(可以相同),则称它为半质数。前几个半质数是 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 2000000
class 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)