本文参考C++版本:如何高效判定、筛选素数

给定n,我们如何输出n以内(不含n)的所有素数?

使用Python,完成函数体,要求返回n以内的素数个数和这些素数。

def countPrimes(n):"""itype -> intrtype -> intrtype -> list"""

关于评价算法速度:如何计算时间复杂度

1.直觉算法

按部就班地遍历2到n-1,依次判断素数。

def countPrimes(n):primes = []if n < 2:return 0, primesfor i in range(2,n):if isPrime(i):primes.append(i)return len(primes), primesdef isPrime(k):"""itype: int, >=2rtype: int"""r_isPrime = Truefor i in range(2,k):if k%i == 0:r_isPrime = Falsereturn r_isPrime
from time import process_time
t1 = process_time()
n = int(input("Input a positive integer: "))
count_primes, primes = countPrimes(n)
t2 = process_time()print("The number of primes within {} is {}:".format(n,count_primes))
print("Elapsed time: {}".format(t2-t1))
Input a positive integer:  10000The number of primes within 10000 is 1229:
Elapsed time: 2.875

评价

直接地,得到时间复杂度(时间复杂读不考虑系数):

但是这样地算法存在冗余,太耗时了。 对n=20,我们只需要检查

  • 20=2*10
  • 20=4*5
  • 20=
  • 20=5*4
  • 20=10*2 观察到只需要检查20在小于
    中的正整数中是否找能被整除的因数,找不到那么后面一半也找不到,找到了就直接判断为素数。

2.平方根算法

检查数k时,遍历因数i范围为

def isPrime(k):"""itype: int, >=2rtype: int"""r_isPrime = Truefor i in range(2,int(k**0.5)+1):if k%i == 0:r_isPrime = Falsereturn r_isPrime
from time import process_time
t1 = process_time()
n = int(input("Input a positive integer: "))
count_primes, primes = countPrimes(n)
t2 = process_time()print("The number of primes within {} is {}:".format(n,count_primes))
print("Elapsed time: {}".format(t2-t1))
Input a positive integer:  10000The number of primes within 10000 is 1229:
Elapsed time: 2.5625

评价

发现优化得效果也没好多少,用时减少一丁点。函数isPrime()时间复杂度为

。 发现其实还有冗余,如果已知2为素数,那么就不应该多余地去检查4,6,8这些2的倍数。按照这样的原理,演化到下一个算法。

素数筛-Sieve of Eratosthenes

筛掉已知素数的倍数。

def countPrimes(n):if n < 2:return 0, primesprime_sieve = [1]*nprime_sieve[0:2] = [0,0]for i in range(2, n):if prime_sieve[i]:for j in range(i*2, n, i):prime_sieve[j] = 0return prime_sieve.count(1), [index for index,value in enumerate(prime_sieve) if value == 1]
from time import process_time
t1 = process_time()
n = int(input("Input a positive integer: "))
count_primes, primes = countPrimes(n)
t2 = process_time()print("The number of primes within {} is {}:".format(n,count_primes))
print("Elapsed time: {}".format(t2-t1))
Input a positive integer:  1000000The number of primes within 1000000 is 78498:
Elapsed time: 0.28125

筛掉倍数后,算法表现提升显著,还有可以提升的空间。 - for i in range(2, n):是在遍历所有小于n大于等于2的正整数,当n=20,我们不需要遍历所有的大于

的正整数,因为不是素数的肯定通过遍历前面一半的时候筛掉了,是素数的素数筛值保持不变。那么可以将遍历范围变为
def countPrimes(n):if n < 2:return 0, primesprime_sieve = [1]*nprime_sieve[0:2] = [0,0]for i in range(2, int(n**0.5)+1):if prime_sieve[i]:for j in range(i*2, n, i):prime_sieve[j] = 0return prime_sieve.count(1), [index for index,value in enumerate(prime_sieve) if value == 1]from time import process_time
t1 = process_time()
n = int(input("Input a positive integer: "))
count_primes, primes = countPrimes(n)
t2 = process_time()print("The number of primes within {} is {}:".format(n,count_primes))
print("Elapsed time: {}".format(t2-t1))
Input a positive integer:  1000000The number of primes within 1000000 is 78498:
Elapsed time: 0.1875

OHHHHHHHHHHHHHHHH 外层的循环范围优化了,现在考虑内层循环for j in range(i*2, n, i):。 当n=20,i=5时会筛掉10,15,20,25...,但是这10,15,20已经被素数2,3筛掉。小于当前素数的因数,要么是更小的素数,要么是更小的素数的倍数,当前素数与这些因数相乘,肯定被筛过了,所以只需要从

开始检查。
def countPrimes(n):if n < 2:return 0, primesprime_sieve = [1]*nprime_sieve[0:2] = [0,0]for i in range(2, int(n**0.5)+1):if prime_sieve[i]:for j in range(i**2, n, i):prime_sieve[j] = 0return prime_sieve.count(1), [index for index,value in enumerate(prime_sieve) if value == 1]from time import process_time
t1 = process_time()
n = int(input("Input a positive integer: "))
count_primes, primes = countPrimes(n)
t2 = process_time()print("The number of primes within {} is {}:".format(n,count_primes))
print("Elapsed time: {}".format(t2-t1))
Input a positive integer:  1000000The number of primes within 1000000 is 78498:
Elapsed time: 0.15625

时间复杂度为:

.

评价

以此来看,本文截至此处目前最好的素数算法为Sieve of Eratosthenes。代码:

def countPrimes(n):if n < 2:return 0, primesprime_sieve = [1]*nprime_sieve[0:2] = [0,0]for i in range(2, int(n**0.5)+1):if prime_sieve[i]:for j in range(i**2, n, i):prime_sieve[j] = 0return prime_sieve.count(1), [index for index,value in enumerate(prime_sieve) if value == 1]

Euler's Sieve

现在介绍一个有线性时间复杂度$mathcal{O(n)}$的算法:欧拉筛法。

算法:

整个过程为:

def countPrimes(n):if n < 2:return 0, primescur_list = list(range(2, n))primes = []while True:cur_prime = cur_list[0]backup_list = cur_list.copy()for j in backup_list:if j*cur_prime not in cur_list:breakcur_list.remove(j*cur_prime)primes.append(cur_prime)cur_list.pop(0)if cur_prime**2 > n:primes.extend(cur_list)breakreturn len(primes), primesfrom time import process_time
t1 = process_time()
n = int(input("Input a positive integer: "))
count_primes, primes = countPrimes(n)
t2 = process_time()print("The number of primes within {} is {}:".format(n,count_primes))
print("Elapsed time: {}".format(t2-t1))

Input a positive integer: 1000 The number of primes within 1000 is 168: Elapsed time: 0.013890345999996612
Input a positive integer: 10000 The number of primes within 10000 is 1229: Elapsed time: 0.36447122299999535
Input a positive integer: 100000 The number of primes within 100000 is 9592: Elapsed time: 49.40181045400001

根据对区区10000的输入,我写的算法就表现得不太行,实际上输入1000000就接近崩溃了,算法本身没问题,我实现的代码不够还原,肯定有很多冗余。这两天折腾够了,以后再看看。


Changelog

  • 2020/04/03: 感谢评论区 @天空和云 的提醒,修改了两处代码。
  • 2020/02/15: 更新了欧拉筛法,思路正确但是代码有缺陷。

1亿以内素数的个数_算法|找出给定范围的所有素数相关推荐

  1. 算法:找出1-10000之间的所有素数

    用试除法 找出1-10000之间的所有素数 若要判断数字n是否为素数,只需将区间 [2,sqrt(n)] 之间的所有整数对数字j进行试除即可. #include <stdio.h> #in ...

  2. 现在有一个整数数组,已知一个数出现的次数超过了一半,请用O(n)的复杂度的算法找出这个数...

    现在有一个整数数组,已知一个数出现的次数超过了一半,请用O(n)的复杂度的算法找出这个数. 方法1:Hash链表 方法2:使用两个变量A和B,其中A存储某个数组中的数,B用来计数.开始时将B初始化为0 ...

  3. 6-10 找出大于num的最小素数 (10 分)

    本题要求实现一个函数:找出大于num的最小素数,并将其作为函数值返回. 函数接口定义: int Prime_number ( int N ); 其中 N 是用户传入的参数. N 的值不超过int的范围 ...

  4. 知哈希算法——找出相似的图片

    知哈希算法--找出相似的图片 感知哈希算法--找出相似的图片 - Create Chen - 博客园 知哈希算法--找出相似的图片 Google 图片搜索功能 在谷歌图片搜索中, 用户可以上传一张图片 ...

  5. Java黑皮书课后题第6章:**6.29(双素数)双素数是指一对差值为2的素数。例如,3和5就是一对双素数,5和7是一对双素数,而11和13也是一对双素数。编写程序,找出小于1000的所有双素数

    6.29(双素数)双素数是指一对差值为2的素数.例如,3和5就是一对双素数,5和7是一对双素数,而11和13也是一对双素数.编写程序,找出小于1000的所有双素数 题目 题目描述 破题 代码 运行示例 ...

  6. Java黑皮书课后题第6章:**6.28(梅森素数)如果一个素数可以写成2^p-1的形式,其中p是某个正整数,那么这个素数就称作梅森素数。编写程序,找出p≤31的所有梅森素数,然后显示如下结果

    6.28(梅森素数)如果一个素数可以写成2^p-1的形式,其中p是某个正整数,那么这个素数就称作梅森素数.编写程序,找出p≤31的所有梅森素数,然后显示如下结果 题目 题目描述 破题 代码 题目 题目 ...

  7. 6-2 找出大于num的最小素数 (16 分)

    本题要求实现一个函数:找出大于m的最小素数,并将其作为函数值返回. 函数接口定义: int Prime_number ( int N ); 其中 N 是用户传入的参数. N 的值不超过int的范围,函 ...

  8. 【c语言】找出大于m的最小素数,并将其作为函数值返回

    /*找出大于m的最小素数,并将其作为函数值返回*/ #include <math.h> #include <stdio.h> int fun(int m) {int i, k; ...

  9. 1亿以内素数的个数_神奇的素数

    数学里面最有趣的问题可能就得说是素数了.世界上最难的问题很多都与素数有关,而且素数又是如此简单的一个概念,只要是学过乘除法的人都能理解什么是素数.如果评选一个非常简单但又极端复杂的数学概念,估计非素数 ...

最新文章

  1. 爬虫python的爬取步骤-Python爬虫爬取数据的步骤
  2. 9.0 C++远征:对象成员
  3. Linux桌面环境介绍以及优缺点分析
  4. 为了使界面组件更圆滑,Swing,且跨系统
  5. popfd指令_2. PUSH 和 POP 指 令 3
  6. 从文件夹内批量获取所有文件名 批处理脚本
  7. 没想到,MyBatis 背后居然用了这么多设计模式
  8. 学编程的人不能不看的好文章啊!!
  9. 拓端tecdat:Python主题建模LDA模型、t-SNE 降维聚类、词云可视化文本挖掘新闻组数据集
  10. Html5简单描述(优点与缺点)
  11. Nginx常见问题(优化)
  12. 计算机中时间服务是哪个,电脑时间不对 Windows时间服务未运行的解决办法
  13. java exe 程序
  14. 关于LSB图片隐写的解法
  15. python iot hub_IoT Hub入门(2)-设备发送消息到云端(d2c)
  16. 力扣765——情侣牵手(贪心+BFS)
  17. 人工智能打造充满创造力的新世界,华为云开发者日无锡站成功举办
  18. 丰和重仓股票本周涨幅
  19. could not acquire a semaphore for execution and no fallback available.
  20. 防抵赖 java_一种防抵赖的架构方法与流程

热门文章

  1. 微软OOXML正式成为国际标准 更名为OXML
  2. 第六节:用audio标签打造一个属于自己的HTML5音乐播放器
  3. 无监督学习 | KMeans与KMeans++原理
  4. ggforce|绘制区域轮廓-区域放大-寻找你的“onepiece”
  5. 这个工具可以组合参数画出2种单细胞Marker显示图
  6. R语言可视化学习笔记之ggridges包绘制山峦图
  7. 如何把笔记本变成显示器_笔记本电脑如何连接使用今声优盒
  8. matlab中asix off_matlab中 hold on 与hold off的用法
  9. centos运行python程序_CentOS 7定时执行python脚本
  10. 递归方法无限级菜单--javascript v1.0