SCAU 8614 素数

孤街浪徒 提交于 2020-03-26 12:40:36

8614 素数

时间限制:500MS  内存限制:1000K 提交次数:0 通过次数:0

题型: 编程题   语言: 无限制

Description

数学对于计算机学是很重要的,大一的高数就曾经(或即将)令一批又一批人饮恨。这里就是一个数学问题,当然,它不需要用到高深的高数知识。
给出n(1<=n<=100000),问1到n之间有几个素数。 

Input

第1行,1个整数T(T<=100000),表示共有T组测试数据
第2---第T+1行,每行1个整数,表示测试数据n 

Output

对于每个测试数据,输出1行,每行1个数,表示1到n之间的素数个数

Sample Input

5
1
2
100
1000
5000

Sample Output

0
1
25
168
669

Source

白衣人

Provider

admin

 

#include <stdio.h>
#include <string.h>
int prime[100001];
int cnt[100001];
int main()
{
    int i,j,t;
    memset(prime,0,sizeof(prime));
    memset(cnt,0,sizeof(cnt));
    prime[0]=prime[1]=1;
    for(i=2;i<100000;i++)
    if(prime[i]==0){
    for(j=i+i;j<100001;j+=i)
    prime[j]=1;
    }
    int ans=0;
    for(i=2;i<100001;i++){
    if(!prime[i]) 
    ans++;
    cnt[i]=ans;
    }
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        printf("%d\n",cnt[n]);
    }
}

解题报告:
因为对我来说是一种打击,不断地超时,一直认为是筛素数出现了问题,而不会去考虑最大的复杂度是多少。印象深刻,我想思维可以有无限的区域,为何要苦苦局限于一点,这点让我知道了自己的不足,也让我坚定了做题的决心,就是为了要锻炼和改变一下逻辑思维能力,这题能学到什么,完全是因人而异的。

下面是另外两种筛素数的方法,仅作参考

Is_prime Code
#include<stdio.h>
#include<string.h>
#include<time.h>
#define MAXN 10000000
int prime[MAXN]; 
int flag[MAXN];
int main()
{
    int i, j, n, m = MAXN, count = 0;
    clock_t start, end;
    start = clock(); 
    memset(prime, 0, sizeof(prime));   //筛素数一 
    prime[0]=prime[1]=1;
    for(i=2;i<MAXN;i++)
    if(!prime[i]){
    for(j=i+i;j<MAXN;j+=i)
    prime[j]=1;    
    }
    end = clock();
    printf("MAXN = %d时 素数筛选法一所用时间:time = %dms\n", m, end-start);
    
   //以下标作为素数,跟下面判断存储素数的方法不同 

    start = clock(); 
    n = 0;
    memset(flag, 0, sizeof(flag));
    memset(prime, 0, sizeof(prime));
    for(i=2; i<MAXN; i++)
    {
        if(!flag[i])   
        {
            prime[n++] = i;
            for(j=i+i; j<MAXN; j+=i)
            flag[j] = 1;
        } 
    }
    end = clock();
    printf("MAXN = %d时 素数筛选法二所用时间:time = %dms\n",m, end-start);
    
    //这里还有可以改进的地方,将j=i+i 换成 j=i*i。但要注意整数溢出! PS对于一个数x,假设它含有质因子i,那么令y=x/i;可以发现,
    //如果所有小于i*i的含有因子i的数字,其y值小于i,这样的话,在以前的筛选过程中,就会把x筛掉
    
    start = clock(); 
    n = 0;
    memset(flag, 0, sizeof(flag));
    memset(prime, 0, sizeof(prime));
    for(i=2; i<MAXN; i++)
    {
        if(!flag[i]) prime[n++] = i;   
        for(j=0; j<n&&prime[j]*i<MAXN; ++j)
        {
            flag[prime[j]*i] = 1;
            if(!(i%prime[j])) break;
        } 
    }
    end = clock();
    printf("MAXN = %d时 素数筛选法三所用时间:time = %dms\n",m, end-start);
    
    //i = 2 flag[2]==0 2是素数,所以存进prime[0] n=1, 在下面的for循环中 flag[2*2=4] = 1;2%2 == 0 break;
    //i = 3 flag[3]==0 3是素数, 存进prime[1] n=2; for循环中: flag[3*2=6] = 1, flag[3*3=9] = 1, 3%3 == 0 break;
    //i = 4 flag[4]==1 4不是素数 n=2;for循环中: flag[4*2 = 8] = 1; 4%2 == 0 break; 
    //说明break的作用:先看看不break会发生什么情况, flag[4*3=12] = 1; 而你也会发现循环到i=6时flag[6*2=12]将会再一次赋值为1 
    //要这样做的原因是想到:每个非素数都有一个最小素因子,而我们的目的是想最优化,必然要除去重复计算的情况,所以根据非素数的
    // 最小素因子判定该非素数(合数)就能除去重复计算的情况, 而代码中,i%prime[j] == 0 时,此时的prime[j]是最小素数,而i可以
    //判定是一个合数,没break这个条件再往下乘的话,那么j里面肯定带有一个相乘后得到的这个数((i*prime[j+1])的最小素因子,而这个
    //可以在另外一种情况prime[`i] = 该最小素因子中判定。比如说flag[6*3 = 18] 和 flag[9*2] = 18, 带break条件就会将flag[6*3 = 18]
    //这种情况忽略, 因为在这之前6%2 == 0。 
    return 0;
} 

输出情况如下:

      哪一种方法较为优化一目了然

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!