素数啊,这玩意儿… 刚学编程那会儿,它可真是个坎儿。听着挺简单,不就是那些除了1和它自己,再没别的正经“朋友”(因数)的数嘛?比如2、3、5、7… 但真让你用Python把它“抓”出来或者“认”出来,脑子就容易打结。尤其是什么“素数判断”啦,“查找一定范围内的素数”啦,感觉怎么写都不对劲,要不就是跑得慢得像蜗牛。别急,咱们今天就聊聊这事儿,保准让你知道素数怎么python玩转得溜溜的。

你想想看,判断一个数是不是素数,最直观的方法是什么?就是试除法对吧?拿它去挨个儿除以比它小的数,从2开始,一直到它前一个数。如果中间发现哪个数能整除它,嘿!对不起,你不是素数!如果一路除下来,一个能整除的都没碰到,那恭喜,你就是个货真价实的素数

代码怎么写呢?喏,最朴素的版本大概长这样:

python
def is_prime_naive(num):
if num <= 1:
return False # 1和比1小的都不是素数
for i in range(2, num): # 从2开始,一直试到 num-1
if num % i == 0: # 如果发现能整除的
return False # 那肯定不是素数
return True # 一路没被整除,就是素数

这段代码,你能说它错吗?逻辑上没毛病,对付小数字,比如判断7是不是素数,它一下就算出来了。is_prime_naive(7)?嗯,range(2, 7)就是2, 3, 4, 5, 6。7除以2余1,7除以3余1,7除以4余3,7除以5余2,7除以6余1。都没整除!return True。完美。

可问题来了,如果我要判断100000007是不是素数呢?(这是个大素数)你的程序会从2一直试到100000006!天哪,这得算到猴年马月去?咖啡泡好几壶,可能还没结果。这效率,简直了!

所以啊,写程序不能只想着“能实现”,更要考虑“怎么更高效地实现”。素数判断这个事儿,其实有个优化过的试除法。你回想一下,一个合数(非素数),它总能分解成两个或多个质因数的乘积。如果它有两个因子a和b,且 a * b = num,那么至少有一个因子(比如a)会小于等于 num 的平方根。你想啊,要是a和b都大于 num 的平方根,那它们的乘积 a*b 岂不是会大于 num?矛盾嘛!

所以,我们根本不需要试除到 num-1,只需要试到 num 的平方根就够了!如果在这个范围内找不到因子,那更大的范围也肯定找不到。平方根之外的因子,都得配对一个平方根以内的因子。

这个优化,能把计算量 dramatically(戏剧性地)降低!代码就变成了这样:

“`python
import math

def is_prime_optimized(num):
if num <= 1:
return False
if num <= 3:
return True # 2和3是素数,直接返回
if num % 2 == 0 or num % 3 == 0:
return False # 排除掉偶数和3的倍数,又快了一点点

# 优化重点:试除到平方根
# 从5开始,步长为6 (5, 7, 11, 13, ...)
# 因为大于3的素数都可以写成 6k ± 1 的形式
# 试除 2 和 3 后,只需检查 5, 7, 11, 13... 这些数
i = 5
while i * i <= num: # 注意这里的条件 i * i <= num
    if num % i == 0 or num % (i + 2) == 0:
        return False # 检查 i 和 i+2 (也就是 6k-1 和 6k+1 的形式)
    i += 6 # 步长为6

return True # 都没整除,那就是素数

“`

你看这个is_prime_optimized函数,它先处理了小于等于3的特殊情况,排除了偶数和3的倍数(这俩占了小数字很大一部分比例,提前筛掉能省不少事)。然后核心部分,它不是傻乎乎地从2开始一个个试,而是从5开始,每次跳跃6步(检查ii+2)。为什么是6步?因为任何大于3的素数都能写成 6k+1 或 6k-1 的形式。比如5=61-1, 7=61+1, 11=62-1, 13=62+1… 这样,我们就不用检查那些肯定是合数的因子(比如4、6、8、9、10、12…),效率又上去了!最关键的是,循环条件变成了i * i <= num,或者写成i <= math.sqrt(num)也行,但乘法通常比开方快一点点,所以i * i <= num更常见些。

学会了判断素数,那怎么查找一定范围内的素数呢?比如找出1到1000之间所有的素数。最简单的方法就是遍历这个范围里的每一个数,然后用我们写好的is_prime_optimized函数去判断它是不是素数,如果是,就把它存起来或者打印出来。

“`python
def find_primes_in_range(start, end):
prime_list = []
for num in range(start, end + 1): # 遍历范围,包含end
if is_prime_optimized(num):
prime_list.append(num)
return prime_list

找出1到100之间的素数

primes_1_to_100 = find_primes_in_range(1, 100)

print(primes_1_to_100)

“`

这段代码清晰明了,就是把判断逻辑套在了一个循环里。对于不太大的范围,这完全没问题。

但是,如果你要找1到100万,甚至1个亿范围内的素数呢?还是用上面的方法,虽然单次判断已经优化了,但你要对这1亿个数都跑一次is_prime_optimized!还是慢!有没有更快的方法,尤其是在查找大量素数的时候?

这时候就轮到“筛法”出场了,最经典的就是埃拉托斯特尼筛法 (Sieve of Eratosthenes)。这个算法的思想特别巧妙:我们不是一个个数去判断“你是不是素数”,而是换个思路:从最小的素数2开始,把所有2的倍数(除了2自己)都标记为“不是素数”;然后找到下一个还没被标记的数,它肯定是素数(比如3),再把所有3的倍数(除了3自己)标记掉;接着是5,把5的倍数标记掉… 这样一路筛下去,最后那些没有被标记掉的数,就全都是素数

想象一下,你有一张纸,上面写着1到N所有的数字。
1. 把1划掉(1不是素数)。
2. 找到第一个没划掉的数——2,它是素数!然后把所有比2大的、2的倍数(4, 6, 8, 10…)都划掉。
3. 找到下一个没划掉的数——3,它是素数!把所有比3大的、3的倍数(6, 9, 12, 15…)都划掉(注意6已经被划掉了,没关系)。
4. 找到下一个没划掉的数——5,它是素数!把所有比5大的、5的倍数(10, 15, 20…)都划掉。
5. …重复这个过程,直到你检查完所有小于等于N的数的平方根(为什么又是平方根?因为如果一个合数N有一个大于其平方根的因子,它必然会有一个小于其平方根的因子,而这个小于平方根的因子在之前的步骤中肯定已经被用来筛掉N了)。

最后,纸上没被划掉的数,就是1到N之间的所有素数

Python来实现筛法,通常用一个布尔值的列表(或者数组),索引代表数字,值代表是否是素数

“`python
def sieve_of_eratosthenes(n):
if n <= 1:
return []

# 创建一个布尔列表,大小为 n+1,is_prime[i] 表示 i 是否是素数
# 初始化所有都为 True (假设都是素数)
is_prime = [True] * (n + 1)
is_prime[0] = is_prime[1] = False # 0和1不是素数

# 从第一个素数2开始筛
# 只需要检查到平方根,原因上面解释过了
# 外层循环变量 p 代表当前的素数
p = 2
while p * p <= n:
    # 如果 is_prime[p] 还是 True,说明 p 是个素数
    if is_prime[p]:
        # 那么就把所有 p 的倍数都标记为 False
        # 从 p*p 开始标记,因为小于 p*p 的倍数(如 2p, 3p, ... (p-1)p)
        # 在 p 之前的素数(2, 3, ...)的筛查中已经被标记过了
        for i in range(p * p, n + 1, p):
            is_prime[i] = False
    p += 1 # 检查下一个数

# 最后,收集所有标记为 True 的索引(数字)
primes = [num for num in range(n + 1) if is_prime[num]]

return primes

找出1到1000之间的素数

primes_up_to_1000 = sieve_of_eratosthenes(1000)

print(primes_up_to_1000)

“`

这个筛法效率,尤其是找大范围的素数时,比挨个判断快多了。因为它每个合数只会被它的最小质因数筛一次。想想看,是不是很聪明?

所以,当你再遇到“素数怎么python”的问题,别慌。首先明确你是要判断一个数,还是要查找一个范围判断的话,用优化过的试除法;查找大范围的话,筛法通常是更好的选择。

编程嘛,不就是这样一点点摸索,一点点优化吗?从最直观的思路开始,发现问题(效率低),然后学习更巧妙的算法筛法),把代码改得更漂亮、跑得更快。每次搞定一个这种经典的小问题,成就感都满满的。Python语言的简洁性,也让这些算法实现起来没那么面目可憎。你看上面的代码,是不是也挺容易读懂的?

记住,写Python解决素数问题,关键在于理解那几个核心的算法思想:朴素试除、优化试除(试到平方根,跳过偶数/3的倍数,甚至跳过6k±1的形式)、以及针对查找大范围素数筛法。把这些思路理清楚,代码自然就写出来了。别怕犯错,多试几次,多看看别人的代码,慢慢就熟练了。这“素数怎么python”的问题,也就迎刃而解了。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。