数论周:
学了几个小东西,这里讲一下找素数。
素数:一个大于1的自然数,除了1和它本身外,不能被其他自然数整除(除0以外)的数称之为素数(质数);否则称为合数。
根据这个性质,判断一个数 n 是否为素数 ,我们可以从 2 ~ n-1 找是否有其因子,如果有,则说明不是素数,return false;反之,return true;
又因为一个数有个小于 sqrt(n)的因子,则一定有一个大于 sqrt(n),反之,如果在 2 ~ sqrt(n)里面没有找到一个因子,那么在sqrt(n)后面也不会有。
bool isprime (int n){
for (int i=2;i*i<=n;i++)
if (n%i==0)
return false;
return true;
}
很方便的判断是否为素数。但如果要进行非常多次判断的话,这种方法将将不再适用,我们要寻求一种更加快速的方法。
因为每个素数只有1和它本身两个因子,所以一个素数的倍数一定不是素数。比如,已知 k 是一个素数,则大于 k 的倍数都不是素数。因此,我们可以把这些数标记成0;
则下次将不再判断。接下来的第一个标记为1的数一定是素数,因为比他小的数的倍数都已经标记完了。
两个优化:
1:在筛除 素数 k 的倍数时,从 k 的 k 倍开始。因为对于 k 的 v (v<k)倍,k*v 一定会在筛除 v 的倍数时 被筛除。
2:由于1的正确性,在枚举已知素数 k 时,只需要枚举到 sqrt(n)的时候,就可以了。
因为当 k > sqrt(n)时, 根据 1中所述,将进行筛除 > n的非素数,这些操作是非必要的。
bool isp[1000000];
void isprime (int n){
memset (isp,true,sizeof(isp));
isp[0]=isp[1]=false;
for (int i=2;i*i<n;i++){
if (isp[i]){
int j=i;
while (j*i<n){
isp[j*i]=false;
j++;
}
}
}
}
来道例题,最近做的素数的题比较少,这是一道“伪”素数:
题目给出一个集合 E 4*n+1 (n是自然数)。里面的数叫作 H-number 。数 H-primes 是在 E 中没有出1和它本身的约数的数,(类似素数)。
给出一个数 n ,求 n 以内有多少数是可以有两个H-primes相乘得到的。
我们可以用类似素数筛法的方法找出所有的 H-primes :5, 9, 13, 17...然后让它们两两想乘,得到数k , v [ k ] 标记成1;然后再用一个数组统计 ans[ k ] k以内有多少这样的数。
/*
*水水更健康
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long llint;
llint ishp[10000020];
llint vhp[10000020];
llint ans[10000020];
llint hp[100000];
void sushu(){
ishp[0]=1;ishp[1]=0;
for (llint i=5;i*i<1000010;i+=4){
if (ishp[i]==0){
llint j=i;
while (j*i<1000002){
ishp[i*j]=1;
j+=4;
}
}
}
llint k=0;
for (llint i=0;i<1000002;i++){
if (ishp[i]==0&&i%4==1){
hp[k++]=i;
// printf ("%d ",hp[k-1]);getchar();
}
}
for (llint i=1;i<k;i++){
for (llint j=i;j<k;j++){
if (hp[i]*hp[j]<1000002){
vhp[hp[i]*hp[j]]=1;
}
else break;
}
}
llint a=0;
for (llint i=0;i<=1000001;i++){
if (vhp[i]==1)
a++;
ans[i]=a;
}
}
int main(){
llint n;sushu();
while (scanf ("%I64d",&n)!=EOF&&n){
printf ("%I64d %I64d\n",n,ans[n]);
}
}