• 素数筛(埃氏筛法与欧拉筛)


    素数筛,其实是将一堆数中的合数给筛掉,留下素数的一个过程。求某个大小范围内的素数个数,是用到素数筛的最最基础的问题。
    首先要给出关于素数的最基本的知识:判断单个数是否为素数。


    判断一个整数n是否为素数

    首先i从2开始枚举到 (sqrt{n}) ,然后一旦n可以被i整除,就返回false,然后如果i枚举完了,n都没能整除i,那就证明是素数,返回true。
    为啥判断到 (sqrt{n}) 就结束了呢?其实这个问题很容易想,如果现在n是10,我判断它是否可以被2整除,发现可以,并且得到的是商5,那么我们后面其实就不需要再去判断10是否可以被5整除了,因为在判断是否为素数的这个情况下,10/2=5本质上和10/5=2没有差别,所以判断到 (sqrt{n}) 就可以结束了,减少重复判断。

    bool isprime(int n){   //判断单个数n是否为素数
    	if(n <= 1) return 0;
    	for(int i=2;i*i<=n;i++)
    		if(n % i == 0) return 0;
    	return 1;
    }
    

    埃氏筛法

    作用就是枚举n以内的素数

    首先将2到n范围内所有的整数写下来,在这其中最小的数字2是素数,将表中所有2的倍数都划去,表中剩余最小的数字是3,它的因数只有它自己,(它已经最小了),然后就再把表中所有3的倍数都划去,以此类推,每次表中剩余的最小数字是m,m就是素数,然后将表中所有m的倍数都划去,这样反复操作之后就能依次枚举n以内的素数。

    模板(数字n的作用就是筛出从1-n的素数,prime数组存这些素数,isprime[ i ]如果为1,则i是素数):

    int prime[n],isprime[n];
    
    int eratosthenes(int n){
    	int p = 0;
    	for(int i=0;i<=n;i++)
    		isprime[i] = 1;//先假设这些数都是素数,后面再由它们的因数来划掉。
    	isprime[0] = isprime[1] = 0;
    	for(int i=2;i<=n;i++){
    		if(isprime[i]){
    			prime[p++] = i;
    			for(int j = 2 * i;j <= n;j += i)
    				isprime[j] = 0;
    		}
    	}
    	return p;
    }
    
    

    埃氏筛法有一个缺陷,每次都要去对当前表内的素数m的所有的倍数进行删掉,也就是“isprime[ j ] = 0” 这个操作,对于一个合数,它很有可能会被很多个素数筛到,也就重复进行了很多次操作,在此基础之上再次进行改进,我们还有欧拉筛法:


    欧拉筛法

    欧拉筛法并不是用目前最小素数的所有倍数来筛合数,而是要筛去一个合数时,用这个合数的最小质因子来筛去它。

    欧拉筛法建立了一个数组prime,然后它的下标从1开始依次存的是这个区间的从小到大的素数,然后在外层循环for中让i累加,这里的i其实就是“倍数”的含义,然后循环的时候,取出数组中所有数,然后从小到大地对于i * 这些合数得到的值筛掉(它们都是合数)然后呢,i一直在增加,每一次执行这个筛操作之前,我们都要先判断一下i是否为素数,若是的话就把i加入前面的数组prime之中。

    在这个筛操作中,还有一个continue操作的条件需要注意:if (i % prime[j] == 0) break;

    这里跳出本次循环直接进行下一次循环的意思是:当i是prime[ j ]的倍数时,i = k * prime[ j ] 如果进行下次循环,j 会加一指到prime数组中的下一个素数,下一次循环中 会出现 i * prime[ j+1 ] 而因为i是prime[ j ] 的倍数,所以上式变为 prime[j] * k * prime[j+1] ,这个表示的是一个合数,而这个合数的最小素因子其实是prime[ j ] ,而不是 prime[j+1] 。也就是说这个时候要筛掉的这个数在前面已经被筛掉了,因为它有一个更小的素因子,所以这已经是一次重复的筛运算了。然后接下来的j递增的内层循环中,i 一直是不变的,所以后面所有的i * prime[ j ] 都是会重复的,故至此跳出本次i的循环,直接进行下一次i的循环了。

    下面prime[0] 表示有多少个素数,然后从数组的下标1开始存第一个素数,以此类推

    代码:

    const int maxn = 100;
    int prime[maxn];
    int vis[maxn];
    void eularsieve(){
    	memset(vis,0,maxn * sizeof(int));//是0表示i是素数
    	memset(prime,0,maxn * sizeof(int));
    	for(int i=2;i<maxn;i++){
    		if(!vis[i])
    			prime[++prime[0]] = i;
    		for(int j=1;j <= prime[0] && i * prime[j] <= maxn;j++){
    			vis[i*prime[j]] = 1;
    			if(i % prime[j] == 0)
    				break;
    		}
    	}
    }
    
  • 相关阅读:
    Java实现 LeetCode 697 数组的度(类似于数组的map)
    Java实现 LeetCode 697 数组的度(类似于数组的map)
    Java实现 LeetCode 697 数组的度(类似于数组的map)
    Java实现 LeetCode 696 计数二进制子串(暴力)
    Java实现 LeetCode 696 计数二进制子串(暴力)
    Java实现 LeetCode 696 计数二进制子串(暴力)
    Java实现 LeetCode 695 岛屿的最大面积(DFS)
    Java实现 LeetCode 695 岛屿的最大面积(DFS)
    PHP serialize() 函数
    PHP print_r() 函数
  • 原文地址:https://www.cnblogs.com/kevin-matrix/p/15139187.html
Copyright © 2020-2023  润新知