线性筛法求素数

线性筛法求素数

题目:给出一个正整数n,打印出所有从1~n的素数(即质数);

 

关键是要找出一个判断一个正整数n是否为素数的方法...


 

傻瓜解法--n,n/2

#include<stdio.h>
int main()
{
    int i,n;
    while(scanf("%d",&n)==1)
    {
        for(i=2; i<n; i++)
            if(n%i==0)
                break;
        if(i==n)
            printf("YES\n");
        else
            printf("NO\n");
    }
}


这是理所当然的想法,按照素数的定义,除了1和它本身没有其他的因数,就是素数。

这种解法的缺点就是红色标注那里,i<n,或者有的是i<n....这种循环规模n稍微大点,运行时间不谈了,都是泪...

 


 

普通解法--sqrt(n)

#include<stdio.h>
#include<math.h>
int main()
{
    int i,n,x;
    while(scanf("%d",&n)==1)
    {
        x=(int)sqrt(n);
        for(i=2; i<=x; i++)
            if(n%i==0)
                break;
        if(i>x)
            printf("YES\n");
        else
            printf("NO\n");
    }
}


这里循环取到sqrt(n),效率改进不少了...但显然还是不够理想....继续往下看


 

普通筛选法--埃拉托斯特尼筛法

先简单说一下原理:

基本思想:素数的倍数一定不是素数
实现方法:用一个长度为N+1的数组保存信息(0表示素数,1表示非素数),先假设所有的数都是素数(初始化为0),从第一个素数2开始,把2的倍数都标记为非素数(置为1),一直到大于N;然后进行下一趟,找到2后面的下一个素数3,进行同样的处理,直到最后,数组中依然为0的数即为素数。
说明:整数1特殊处理即可。

举个例子,N=20时,演示如下图:

最后数组里面还是0的就是素数了...

代码实现如下:

prime[]用来保存得到的素数 prime[] = {2,3,5,7,11,.........} tot 是当前得到的素数的个数 check :0表示是素数  1表示合数

memset(check, 0, sizeof(check));
int tot = 0;
for (int i = 2; i <= n; ++i)
{
    if (!check[i])
    {
        prime[tot++] = i;
    }
    for (int j = i+i; j <= n; j += i)
    {
        check[j] = 1;
    }
}


此筛选法的时间复杂度是O(nloglogn) 空间复杂度是O(n)

不足之处也比较明显,手动模拟一遍就会发现,很多数被处理了不止1遍,比如6,在素数为2的时候处理1次,为3时候又标记一次,因此又造成了比较大的不必要处理...那有没有改进的办法呢...就是下面改进之后的筛法...

 


 

线性筛法--欧拉筛法

先上代码吧...

#include<cstdio>
#include<cstring>
#define MAXN 100005
#define MAXL 1299710
int prime[MAXN];
int check[MAXL];

int tot = 0;
memset(check, 0, sizeof(check));
for (int i = 2; i < MAXL; ++i)
{
    if (!check[i])
    {
        prime[tot++] = i;
    }
    for (int j = 0; j < tot; ++j)
    {
        if (i * prime[j] > MAXL)
        {
            break;
        }
        check[i*prime[j]] = 1;
        if (i % prime[j] == 0)
        {
            break;
        }
    }
}


精华就在于红色标注那两处,它们保证每个合数只会被它的最小质因数筛去,因此每个数只会被标记一次,所以时间复杂度是O(n)

还是按上面的例子进行一遍模拟:N=20

此过程中保证了两点:

1、合数一定被干掉了...

2、每个数都没有被重复地删掉

(证明 见参考资料)


 

引申--求欧拉函数

在数论,对正整数n,欧拉函数是小于或等于n的数中与n互质的数的数目。此函数以其首名研究者欧拉命名,它又称为Euler's totient function、φ函数、欧拉商数等。 例如φ(8)=4,因为1,3,5,7均和8互质。

求欧拉函数的方法只需在上面的程序中稍有改动即可,此处只贴出代码:

#include<cstdio>
#include<cstring>
#define MAXN 100005
#define MAXL 1299710
int prime[MAXN];
int check[MAXL];
int phi[MAXL];
int tot = 0;
phi[1] = 1;
memset(check, 0, sizeof(check));
for (int i = 2; i < MAXL; ++i)
{
    if (!check[i])
    {
        prime[tot++] = i;
        phi[i] = i - 1;
    }
    for (int j = 0; j < tot; ++j)
    {
        if (i * prime[j] > MAXL)
        {
            break;
        }
        check[i*prime[j]] = 1;
        if (i % prime[j] == 0)
        {
            phi[i*prime[j]] = phi[i] * prime[j];
            break;
        }
        else
        {
            phi[i*prime[j]] = phi[i] * (prime[j]-1);
        }
    }
}


若是素数,那么从1~n-1都是和它互质的数,所以phi(i) = i - 1;

另外两个是积性函数(见参考资料)的公式和欧拉函数的特性。


 

参考资料

1、http://suno.cnblogs.com/

2、http://wenku.baidu.com/view/1187eebce009581b6ad9eb12

3、http://blog.csdn.net/dinosoft/article/details/5829550

ps:
实例:
代码:
#include<stdio.h>
const int N=2000010;
int p[N/10],c[N];
void is_sushu()
{
    int tot=0,i,j;
    c[0]=1,c[1]=1;
    for(i=2;i<=N;i++)
    {
        if(!c[i])
            p[tot++]=i;
        for(j=0;j<tot&&i*p[j]<N;j++)
        {
            c[i*p[j]]=1;
            if(!(i%p[j]))
                break;
        }
    }
}
int main()
{
    int n;
    is_sushu();
    while(~scanf("%d",&n)&&n)
    {
        int i;
        for(i=0;p[i]<=n;i++)
        {
            printf(" %d",p[i]);
        }
        printf("\n");
    }
    return 0;
}

ps:上述线性筛法超时。用一般筛法却过了。坑了有没有。。
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 线性筛是一种常用的求素数的方,它的思想简单且高效。下面是使用Java语言实现线性筛求素数的代码: ```java import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class LinearSieve { public static List<Integer> getPrimes(int n) { boolean[] isComposite = new boolean[n + 1]; List<Integer> primes = new ArrayList<>(); for (int i = 2; i <= n; i++) { if (!isComposite[i]) { primes.add(i); } for (int j = 0; j < primes.size() && i * primes.get(j) <= n; j++) { isComposite[i * primes.get(j)] = true; if (i % primes.get(j) == 0) { break; } } } return primes; } public static void main(String[] args) { int n = 100; List<Integer> primes = getPrimes(n); System.out.println("从 2 到 " + n + " 的素数为:"); for (int prime : primes) { System.out.print(prime + " "); } } } ``` 以上代码中,我们使用了一个布尔数组`isComposite`来标记是否为合数。初始时,将所有数都标记为非合数。然后从2开始,遍历到n,如果某个数i是合数,则跳过;如果是素数,则将其加入到素数列表中,并标记它的倍数为合数。遍历结束后,我们得到了从2到n的所有素数。 在main函数中,我们设置n为100,调用`getPrimes`函数获取从2到100的素数,并打印出来。 运行结果为:从 2 到 100 的素数为:2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97。 ### 回答2: 线性筛是一种高效地筛选出一定范围内的素数的算。下面是用Java实现线性筛求素数的代码: ```java import java.util.*; public class LinearSieve { public static List<Integer> sieve(int n) { boolean[] isPrime = new boolean[n + 1]; Arrays.fill(isPrime, true); // 将所有数初始化为素数 List<Integer> primes = new ArrayList<>(); for (int i = 2; i <= n; ++i) { if (isPrime[i]) { primes.add(i); // 将素数加入结果列表 } for (int j = 0; j < primes.size() && i * primes.get(j) <= n; ++j) { isPrime[i * primes.get(j)] = false; // 将当前素数倍数标记为非素数 if (i % primes.get(j) == 0) { break; // 若当前数为素数倍数,跳出内层循环 } } } return primes; } public static void main(String[] args) { int n = 100; // 范围上限 List<Integer> primes = sieve(n); System.out.println("范围[2, " + n + "]内的素数有:"); for (int prime : primes) { System.out.print(prime + " "); } } } ``` 通过线性筛,我们首先将所有数初始化为素数,然后从2开始,将每个素数的倍数标记为非素数,直到筛选结束。最后,将筛选出的素数存入结果列表中。在上述代码中,我们以100为例,调用`sieve`方求解范围内的素数,并输出结果。 当我们运行上述代码时,将会得到范围[2, 100]内的素数列表: ``` 范围[2, 100]内的素数有: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 ``` 以上就是使用Java实现线性筛求素数的代码及结果。 ### 回答3: 线性筛是一种用于求解素数的算,可以高效地找出某一个范围内的所有素数。下面是使用Java语言实现线性筛求素数的代码: ```java import java.util.ArrayList; import java.util.List; public class PrimeNumbers { public static List<Integer> getPrimeNumbers(int n) { List<Integer> primeNumbers = new ArrayList<>(); boolean[] isComposite = new boolean[n + 1]; for (int i = 2; i <= n; i++) { if (!isComposite[i]) { primeNumbers.add(i); } for (int j = 0; j < primeNumbers.size() && i * primeNumbers.get(j) <= n; j++) { isComposite[i * primeNumbers.get(j)] = true; if (i % primeNumbers.get(j) == 0) { break; } } } return primeNumbers; } public static void main(String[] args) { int n = 100; List<Integer> primeNumbers = getPrimeNumbers(n); System.out.println("在[2, " + n + "]范围内的素数有:"); for (int number : primeNumbers) { System.out.println(number); } } } ``` 这段代码使用了一个布尔数组isComposite来记录某个数是否为合数(非素数),初始时假设所有数都是质数,然后从2开始遍历到n,如果某个数i没有被标记为合数,就将其添加到素数列表中,并将i与已有的质数依次相乘,将其标记为合数。 运行以上代码,可以求解出2到100之间的所有素数。输出结果如下: ``` 在[2, 100]范围内的素数有: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 ``` 通过线性筛,我们可以高效地找到某个范围内的素数,而不需要遍历所有的数进行判断。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值