• 数学技巧之素数筛选


    如果只是对一个整数进行素性测试的只要o(√n)的复杂度便可以判定,蓝而如果是n个呢(n<=1000)照样可以,那如果100000个呢?对于普通的o(n√n)根本跑不动,因此我们必须寻找更加高效的算法,常用的筛选方法有埃氏筛法, 区间筛法,欧拉筛法。

    1.埃氏筛法

    首先,我们先把2-n范围内的数写下来,其中最小的素数是2,那么能被2整除的数便不是素数,那么我们可以把2的倍数都划去。然后剩下的最小素数便是3,我们便把3的倍数都划去,以此类推。这样反复操作我们就能枚举n以内的素数。

    贴上伪代码:

    1 const int N = 1000000 + 5;
    2 int check[N], prime[N];
    3 
    4 int ptot = 0;
    5 memset(check, 0, sizeof(check));
    6 for(int i = 2; i <= n; i ++){
    7     if(!check[i])prime[ptot++] = i;
    8     for(int j = 2; i * j <= n; j ++) check[i * j] = 1;
    9 }

    但是问题又来了, 这个复杂度究竟该怎么算呢?怎么看像o(n2)呢?No No No~~

    每次我们筛去每个数的整数倍像2筛了n/2个,3筛了n/3个;

    因此总的复杂度约为:

    哎呀!这不是发散级数吗?这个不是发散的数啊,那复杂度不是会爆掉吗?

    别担心,虽然它是发散级数,但是他的增长速度非常慢,来我们测试一下

    看到了吗?这个发散级数在n = 1000000的时候都没有超过14由此可见它的复杂度很小,近似等效成o(nloglogn),而对我们这些搞ACM的人来讲他大致看成线性的都无妨。

    2.区间筛法

    设区间[a,b)表示a<=x<b,的素数所构成的集合。

    我们很容易知道b只要不能整除[2,√b]里面的素数,那么b就可以被认定为一个素数,因此,如果我们有√b里面的素数表,便可以结合埃氏筛法来判断。

    附上伪代码:

     1 const int N = 100000 + 5;
     2 int check[N], prime[N];
     3 int is_prime[N];
     4 
     5 int ptot = 0;
     6 memset(check, 0, sizeof(check));
     7 memset(is_prime, 0, sizeof(is_prime));
     8 for(int i = 2; i <= n; i ++){
     9     if(!check[i])prime[ptot++] = i;
    10     for(int j = 2; i * j <= sqrt(b); j ++) check[i * j] = 1;//筛[2,√b)
    11     for(int j = a/i+1; i * j < b; j ++) is_prime[i * j - a] = 0;//筛[a,b)
    12     //将[a, b)的数,移到[0, b - a)可节省空间
    13 }

    3.欧拉筛法

    这便是重头戏,真真正正的o(n)的复杂度,不说什么,直接上代码。

     1 const int N = 1000000 + 5;
     2 int prime[N], check[N];
     3 
     4 memset(prime, 0, sizeof(prime));
     5 memset(check, 0, sizeof(check));
     6 int ptot = 0;
     7 for(int i = 2; i <= n; i ++){
     8     if(!check[i]) prime[ptot ++] = i;
     9     for(int j = 0; j < ptot; j ++){
    10         if(prime[j] * i > n) break;
    11         check[prime[j] * i] = 1;
    12         if(i % prime[j] == 0) break;
    13     }
    14 }

    这个貌似只是做了少许改进了吧,貌似就改进了第12行啊,聪明,蓝而就是因为这一行,导致了他的复杂度变成完美的线性, 设合数的最小质因数为p,则当它遍历到这个素数的时候因为第12行二跳出循环,这样直接导致了每个合数只能被它的最小质因数给删去。

    下面给出证明:

    设合数n的最小质因数为p,另一个比它大的质因数为p1,设 n = p1 * m1 = p * m,我们很容易就可以发现j循环到质因数p的时候合数n第一次被标记(若循环到p之前已经跳出循环则说明有比p更小的质因数),若也被p1标记,则是在这之前(因为p1 > p,所以m1 < m),考虑i循环到m1,注意到n = p*m=p1*m1且p,p1为不同的质数,因此p|m1,所以当j循环到质数p后就结束,不会循环到p1.这就说明了不会被p1筛去,说明合数只能被它最小的素数给删去。(很神奇吧!)

    既然要做,那就好好做! 自己选的路,自己走完!
  • 相关阅读:
    Day 03
    Day 02
    Day 01
    re正则表达四
    python学习05之数据可视化
    python学习04之柱形图和热图
    python学习03之线图表
    python的学习02之数据处理
    python的学习01之csv文件处理
    中国大学排名实例
  • 原文地址:https://www.cnblogs.com/zyf0163/p/4734985.html
Copyright © 2020-2023  润新知