筛法求素数(质数)

  • 试除法 O(√n)

 bool isPrime (int x)  //x是否为素数
 {
     for (int i = 2; i < x; i++) 
     {
         if (x % i == 0)
             return false;
     }
     return true;
 }

 

改进:

  寻找x是否存在新的因子,设新的因子为a, 则可求出另一个为b

  x = a * b;  令 a <= b

  b = x /a;

  所以,a的范围最大和b相等。即,a最大为sqrt (x)。//需要#include <cmath>

 bool isPrime (int x)  //x是否为素数
 {
     for (int i = 2; i <= sqrt(x); i++) //或写成 i * i <=x
     {
         if (x % i == 0)
             return false;
     }
     return true;
 }
  • 埃氏筛法 O (n logn)

首先是素数的Def:

  一个大于1的自然数,除了1和它自身外,不能整除其他自然数的数叫素数;否则叫合数。

  每个合数都可以写成几个质数相乘的形式,这几个质数就叫做这个合数的质因数。

唯一分解定理:

  任何一个大于1的自然数N,若N不为质数,那么N可以唯一分解成有限个质数的乘积。

埃氏筛法:

  根据定义可以得到,合数是某个质数的倍数。那么在数表中,把质数的倍数都去掉,剩下的就都是质数了(得到质数表)

#include <iostream>
#include <cstring>  //memset()
using namespace std;

//等价于flag 相当于标记作用 
bool isPrime[100000005]    ;  //把下标当作数字(范围)  //isPrime[x]  true:x是质数  false: x不是质数  

//正确标记n以内数字的质数状态,得到质数表 
void aiPrime(int n)
{
    memset(isPrime, true, sizeof(isPrime)); //初始化全为true,后续把质数的倍数判断为false 
    isPrime[0] = isPrime[1] = false;  //特殊情况 
    
    for(int i = 2 ; i <= n; i++)
    {
        if (isPrime[i]) //是质数,将质数的倍数筛出去 
        {
            for( int j = 2; i * j <=n; j++)
            {
                isPrime[i *j] = false;
            }
        }
    }    
}

int main()
{
    int n, cnt = 0;
    cin >> n;
    aiPrime(n); //调用后isPrime[]中质数都为true,合数都为false 
    for (int i = 1; i <= n; i++)
    {
        if(isPrime[i])
            cnt++;
    }
    cout << cnt; //输出质数个数 
    return 0;
}
  • 欧拉筛法 (接近 O(n)

在埃氏筛法中,不同质数的合数可以是同一个,所以在筛选时会有重复。

根据唯一分解定理,只要能唯一地构造出质数序列乘积,每个合数只筛选一次,就是欧拉筛法。

x = a * b * c (a b c的排列方式多样

则规定 a <= b < = c,由此唯一确定一个序列

若存在质数P

P * i = P * a * b * c,有 P <= a <= b <= c,即质数P小于等于i的最小质因子

 

i 质数表 筛除的合数
2 {2} 2 * 2 = 4
3 {2, 3}

2 * 3 = 6

3 * 3 = 9

4(2 * 2 {2, 3}

2 * (2 * 2) = 8

5 {2, 3, 5}

2 * 5 = 10

3 * 5 = 15

5 * 5 = 25

6(2 * 3 {2, 3, 5} 2 * (2 * 3) = 12
7 {2, 3, 5, 7} {14, 21, 35, 49}
8(2 * 2 * 2) {2, 3, 5, 7} 2 * (2 * 2 * 2) = 16
9(3 * 3) {2, 3, 5, 7} {18, 27}

  

#include <iostream>
#include <cstring>  //memset()
using namespace std;

int prime[100000005];  //存放过程中找到的质数(质数表) 
bool isPrime[100000005]; // 标记数组

int erla(int n)
{
    memset(isPrime, true, sizeof(isPrime));
    isPrime[0] = isPrime[1] = false;
    int cnt = 0;
    
    for(int i = 2; i <= n; i++)
    {
        if(isPrime[i])
        {
            prime[cnt] = i;   
            cnt++;            //与上一行可写成 prime[cnt++] = i; 
        }
        //构造质数序列, 质数P * i , P <= i的最小质因子 
        for(int j = 0; j < cnt && prime[j] * i <= n; j++)
        {
            isPrime[prime[j] * i] = false;
            if(i % prime[j] == 0)  //判断p是否 <= i的最小质因子 
                break;
        }
    }
    return cnt;
} 

int main()
{
    int n;
    cin >> n;
    cout << erla(n);
    return 0;
}

 

  

posted @ 2021-07-31 12:13  白藏i  阅读(75)  评论(0)    收藏  举报