前言:众所周知,查找素数是算法题中最基础的问题,也是经常被问到的问题。
但往往同学们找不到最优解法,因而导致时间复杂度过大而超出限制。
下面列出常用的求素数方法配上所对应的时间复杂度来说明欧拉筛算法的优势:
常见方法:
1.暴力求素数 时间复杂度 : O(n^2) 。
【优化一下:缩小数据范围从 n 优化到√n。求开方,时间复杂度 : 自然也就从 O(n2) 到 O(√n) 】。
2.著名的埃式筛法 时间复杂度 : O(nlogn)。
3.欧拉筛法 时间复杂度为: O(n)。
讲解:对于每个数x,对于所有小于x的素数p而言,将x*p筛掉。遇到x%p==0的情况下会直接跳出。可以证明,每个合数一定会被这种筛法筛选一次(假设x的最小因子是p,那么在筛 x/p 这个数的时候,筛到p的时候会跳出,这样正好筛到了x),且一定只被筛了一次。所以是线性复杂度。为O(n)。
下面测试筛选100以内的素数 (c#语言)
整体代码 + 模板 (方便记忆) :
class 欧拉筛{static void Main(String[] args){int max = 100; //求100以内的素数;List<int> prime = new List<int>(); //定义列表存储100以内的素数; bool[] vis = new bool[max];//判断是否已经已经标记,若标记即不是素数;//筛选int index = 0;//定义索引for(int i = 2;i < max; i++) //素数最小值为2,从2开始{if(!vis[i]){prime.Add(i); index++;}//两个判定:保持j在已有素数数量的范围内活动,也保持 标记数目 保证 在 最大讨论数目范围内。for(int j = 0; j < index && prime[j] * i < max;j++) {vis[i * prime[j]] = true;if (i % prime[j] == 0)//防止重复标记{break;}}}foreach (var k in prime){Console.Write(k + " ");}Console.Read();}}
得到结果: 与答案保证一致。
详细讲解 : if (i % prime[j] == 0) break;//防止重复标记
有些同学对于 下面这行代码不是很了解,下面举例解释一下:
欧拉筛法时间复杂度少的原因,就是它不会重复标j记一个数是不是素数的倍数
例如:i = 4 ,j = 0,prime[0] = 2,i * prime[0] = 8 ; i % prime[0] = 0,( 4%2 = 0)所以退出循环 。j那么为什么这里要退出循环呢?如果不跳出循环:
prime[j+1] = 3,i=4 , i * prime [j+1] = 4 * 3 = 2 * 6 = 12,
但在i = 6时进入循环,当 j = 0 时,i * prime [j] = 2 * 6 = 12。
(此时就会重复标记,达不到只筛选一次的结果)。
希望本文章可以帮助到正在学算法的你,若觉得本文文章不错,希望点赞收藏,若文章有错误或者不明白的地方,欢迎交流讨论~~
详细篇
如果觉得《数据结构与算法:欧拉筛——查找素数(质数)的最优解算法 O(n)》对你有帮助,请点赞、收藏,并留下你的观点哦!