素数判定算法优化

75 篇文章 0 订阅

常规写法亲民,高效写法炼人。用素数的基本特性写,易读易懂;用6k±1特性写,高效但却得有学过《数论》。


(笔记模板由python脚本于2024年05月09日 19:47:00创建,本篇笔记适合初通Python,熟悉六大基本数据(str字符串、int整型、float浮点型、list列表、tuple元组、set集合、dict字典)的coder翻阅)


【学习的细节是欢悦的历程】


  自学并不是什么神秘的东西,一个人一辈子自学的时间总是比在学校学习的时间长,没有老师的时候总是比有老师的时候多。
            —— 华罗庚


等风来,不如追风去……


常规写法亲民
素数判定的优化
(高效写法炼人)


本文质量分:

97 97 97

本文地址: https://blog.csdn.net/m0_57158496/article/details/138718910

CSDN质量分查询入口:http://www.csdn.net/qc


目 录

  • ◆ 素数判定的优化
    • 0、“高级”代码
    • 1、最平民的写法
    • 2、算法优化
      • 2.1 剔除偶数
      • 2.2 平方根特性
      • 2.3 6k±1规则
      • 2.4 素数列表
      • 2.5 代码解析
    • 3、结语
    • 4、我关于“素数”的学习笔记
    • 5、完整源码(Python)


◆ 素数判定的优化


0、“高级”代码


  • ‘高效的素数判定算法’
    在这里插入图片描述
      当我读懂这段代码的时候(这是一个非法高效的算法,找出106以内的所有素数,用时不到6秒),就有些感叹了。解决“问题”的代码也和写文章的词句一样,也是要“推敲”的,在不同的语境表达不一样的“情感”。所以,有了这篇笔记。😋



回页目录


1、最平民的写法


  素数是一个大于1的自然数,除了1和它本身以外不再有其他因数。换句话说,如果一个数只能被1和它本身整除,那么这个数就是素数。例如,2、3、5、7、11、13、17、19、23等都是素数。


  根据素数的定义可以写法判断素数的基本算法。 这是最简单直接的方法。对于给定的数n,如果n不是2,则检查从2到n的所有整数是否能整除n。如果能被整除,则n不是素数;如果都不能整除,则n是素数。


python代码


def isPrime(num: int) -> bool:
    ''' 素数判定 '''
    if num < 2:
        return False # 小于2的都不是素数
    
    for i in range(2, num):
        if num%i == 0:
            return False # 当num有1和自身以外的因子,不是素数
            
    return True # 经过前面的排查,就只剩素数了


  • 效率
    在这里插入图片描述


回页目录


2、算法优化


2.1 剔除偶数


  除2之外,偶数都不是素数。据此特性,可以首先剔除偶数,减少试除次数而增效。


Python代码


def isPrime(num: int) -> bool:
    ''' 素数判定 '''
    if num < 2:
        return False # 小于2的都不是素数
    if num == 2: return True # 2是素数
    elif num%2 == 0: return False # 剔除偶数
    
    for i in range(3, num, 2): # 只试除大于2的奇数
        if num%i == 0:
            return False # 当num有1和自身以外的因子,不是素数
            
    return True # 经过前面的排查,就只剩素数了


  • 效率
    在这里插入图片描述
    减少了一半试除,增效一倍。



回页目录


2.2 平方根特性


  一个自然数的因子,必定不大于其平方根。因为如果自然数n有一个因子大于sqrt(n),那么必定有一个因子小于sqrt(n)。
据此,可以优化算法,提升效率。


Python代码


def isPrime(num: int) -> bool:
    ''' 素数判定 '''
    if num < 2:
        return False # 小于2的都不是素数
    
    for i in range(2, num):
        if i*i > num: break # 一个数的因子,必定小于等于其平方根
        if num%i == 0:
            return False # 当num有1和自身以外的因子,不是素数
            
    return True # 经过前面的排查,就只剩素数了


  • 效率
    在这里插入图片描述
    减少了试除的一半(当被判定的数是素数时),次数效率提升了近50倍。



回页目录


2.3 6k±1规则


  所有的素数除了2和3都形如6k±1的形式,其中k是非负整数。这个规则可以减少试除法中需要检查的数的数量。


Python代码


def isPrime(num: int) -> bool:
    ''' 素数判定 '''
    if num < 2:
        return False # 小于2的都不是素数
    if num == 2 or num == 3: return True # 2和3都是素数
    elif num%2 == 0 or num%3 == 0: return False # 剔除2、3的倍数

    for i in range(5, int(num**0.5)+1, 6): # 只检查6k±1的自然数
    
        if num%i == 0 or num%(i+2) == 0:  
            return False # 大于5的素数都满足6k±1的形式,6k形式的数一定是合数
            
    return True # 经过前面的排查,就只剩素数了


  • 效率
    在这里插入图片描述
    在这里插入图片描述
    这即是,开篇图片中的“最佳算法”。



回页目录


2.4 素数列表


  找出限定范围内的所有素数,如果有上限平方根以内的素数列表,+上6k±1规则,可以更高效的“运作”。如查找106以内的所有素数:


Python代码


def isPrime(num: int) -> bool:
    if num > 10**6-1:
        input(f"\n{' 本函数仅限于查找10^6以内的素数 ':~^28}")
        return # 退出代码执行返回命令行
    if num < 2:
        return False # 剔除2以下的数
    if 1 < num < 4:
        return True
    if num%2 == 0 or num%3 == 0:
        return False
    myprimes = (5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997) # 1000以内大于5的素数,由于前面已剔除2、3的倍数,就不再试除2、3。本列表仅限于查找100w以内的素数
    myprimes = iter(myprimes)

    for i in myprimes:
        if i*i > num:
            break 
        if num%i == 0:
            return False
        
    return True


#isPrime(1000_000+1) # 调试报错提示


  • 效率
    在这里插入图片描述
      由试炼可鉴,用时比6k±1规则又缩短了近1/4。

  • 报错提示调试
    在这里插入图片描述



回页目录


2.5 代码解析


  • 对2.3节代码的评价
    1行:函数定义,接受一个整数参数 num,并返回一个布尔值。
    2行:函数的文档字符串,简要描述了函数的功能。
    4行:如果 num 小于2,直接返回 False,因为素数定义为大于1的自然数。
    6-7行:如果 num 等于2或3,直接返回 True,因为2和3都是素数。
    9行:如果 num 是2或3的倍数,返回 False,因为这些数都是合数。
    11行:这里开始了一个 for 循环,循环的范围是从5到 int(num**0.5)+1,步长为6。这个循环的设计是高效的,因为它只检查了可能是素数的一部分数字。所有大于3的素数都可以表示为6k±1的形式,其中k是自然数,因为所有形如6k的数都可以被2和3整除,所以不可能是素数。
    12-13行:在循环内部,检查 num 是否能被 ii+2 整除。如果能,那么 num 不是素数,返回 False。
    15行:如果循环结束都没有找到能整除 num 的数,那么 num 是素数,返回 True。

      总体来说,这段代码是高效的,因为它避免了不必要的检查,并且利用了素数的性质来减少计算量。这种方法的效率很高,尤其是对于较大的数。代码的逻辑清晰,注释也很好地解释了每一步的目的。这是一个很好的素数判定函数的实现。

  • 对2.4节代码的解析
    1-4行:首先检查输入的数值是否超过函数的处理范围(10^6 - 1)。如果是,则打印一条消息并退出函数,返回 None。这里使用了一个格式化字符串来打印居中的消息,并且使用了脱字符号 ~ 来填充空白。
    6行:如果输入的数值小于2,直接返回 False,因为2是最小的素数。
    8行:对于2到4之间的数,直接返回 True,因为它们都是素数。
    10行:如果 num 是2或3的倍数,则返回 False,因为合数肯定能被2或3整除。
    12行:定义了一个包含小于1000的所有素数的元组 myprimes,这些素数都是大于5的。之所以只需要这些素数,是因为前面的代码已经排除了2和3的倍数。
    13行:通过 iter 函数将 myprimes 转换为一个迭代器。这样做是为了在接下来的循环中逐个使用这些素数。
    15-17行:使用一个 for 循环遍历 myprimes 中的素数。循环中有一个判断,一旦当前素数的平方大于 num,就结束循环。这是因为如果一个数不是素数,那么它必定有一个因子是小于或等于它的平方根的。
    18行:在循环中,如果 num 能够被当前素数整除,那么 num 不是素数,函数返回 False。
    20行:如果循环结束都没有找到能整除 num 的素数,那么 num 是素数,函数返回 True。

      整体来看,这个函数通过排除法来确定一个数是否为素数。它首先排除那些明显不是素数的数(小于2的数和2或3的倍数),然后通过只检查小于1000的素数来减少计算量。这种方法的效率相对较高,尤其是对于较小的数。然而,对于非常大的数,这种方法可能不是最优的,因为它需要预先定义一个相对较大的素数列表。



回页目录


3、结语


  代码没有“最好”只有“更好”,根据实际需求选择适宜的算法和相应的“代码表现形式”,才是“代码编撰”最正确的打开方式。



回页目录


4、我关于“素数”的学习笔记


  • 11
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦幻精灵_cq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值