嗨,各位码农朋友、或者只是对编程世界有点好奇的伙伴们,今天咱们聊点啥呢?就那个听起来带点数学味儿,但用 Python 玩起来特有意思的——怎么 弄质数 这件事儿!说实话,刚开始接触编程,或者说刚学到循环和判断那会儿,写个程序判断是不是 质数,或者找出一定范围内的 质数,简直是教科书级别的经典案例,有点像编程界的“Hello, World!”进阶版。别看它简单,里头门道可不少,尤其你想把它 得又快又好,那才见真本事呢。

质数 这玩意儿,大家中学都学过吧?就是那些大于1,除了1和它自己以外,再也找不出别的正整数能整除它的数。比如2、3、5、7、11… 那个1可不是 质数 哦,这个要记住,是个小坑。为什么用 Python弄质数 呢?原因特简单, Python 语法够简洁,写起来像说人话,而且处理这种数学逻辑问题,简直是得心应手,效率也还不错(当然,跟C这种比还是差点,但在大多数场景下足够用了)。更重要的是,你能把精力放在思考 算法 本身,而不是被繁琐的语法绊住手脚。

那, python怎么弄质数 呢?这事儿得分两步说:第一步,怎么判断一个数是不是 质数?第二步,怎么找出一定范围内的所有 质数

咱们先说第一步,判断单个数字是不是 质数

最直观、最“暴力”的方法是啥?给个数字n,我就从2开始,一个一个往上试,看看有没有哪个数能整除n。如果找到一个能整除的(除了n本身),那它肯定不是 质数 了,对吧?一直试到n-1。如果一个都没找到,那恭喜,这个数就是 质数

写成 Python 代码,大概是这样(当然,得考虑n小于等于1的情况,它们都不是 质数):

python
def is_prime_v1(n):
if n <= 1:
return False # 1和小于1的都不是质数
for i in range(2, n): # 从2试到n-1
if n % i == 0: # 如果能整除
return False # 那就不是质数
return True # 实在找不到,那就是质数

这个方法吧,简单粗暴,逻辑清晰,刚学的时候这么写完全没问题。但你有没有想过,如果n是个特大的数,比如几亿甚至上百亿,你这么一个一个试过去,得试多少次啊?电脑就算再快也受不了啊!这效率,简直是“龟速”!

所以,得优化!怎么优化?你想啊,如果一个数n不是 质数,它肯定能被某个小于它的数整除。这个能整除它的数,我们叫它因子。如果n有个因子a,那n就能写成n = a * b。这里头,ab总有一个小于等于sqrt(n)sqrt就是平方根)。为啥?要是ab都大于sqrt(n),那a * b肯定就大于sqrt(n) * sqrt(n),也就是大于n了,这跟a * b = n矛盾啊!所以,我们根本不用试到n-1那么远,只需要试到sqrt(n)就行了!如果在这个范围内找不到因子,那外面更大的范围也肯定找不到了。

这个优化可就厉害了,一下子把检查范围缩小了不知道多少倍!判断100以内的数,原来要试到99,现在只需要试到10(100的平方根是10);判断100万以内的数,原来要试到999999,现在只需要试到1000!这效率提升,简直是坐火箭!

Python 实现呢?sqrt(n)可以用n**0.5来算,然后取整。记住,检查范围是到平方根,包含平方根本身,所以range的结束点得加1。

python
def is_prime_v2(n):
if n <= 1:
return False
# 优化:只需要检查到sqrt(n)
limit = int(n**0.5) + 1 # 注意这里要加1,保证检查到平方根
for i in range(2, limit):
if n % i == 0:
return False
return True

这还没完!还能再优化!除了2以外,所有的偶数都不是 质数。那我们判断的时候,除了2这个特殊情况,是不是可以只检查奇数因子呢?当然可以!

看这个数n,如果它是2,那它就是 质数。如果它是偶数(大于2),直接判定不是 质数。如果它是奇数,那它的因子也只可能是奇数。所以,我们只需要从3开始,只检查3、5、7… 这些奇数,一直到sqrt(n)。每次步长不是1,而是2!

python
def is_prime_v3(n):
if n <= 1:
return False
if n == 2: # 2是质数
return True
if n % 2 == 0: # 大于2的偶数不是质数
return False
# 优化:只需要检查到sqrt(n)的奇数
limit = int(n**0.5) + 1
# 从3开始,步长为2(只检查奇数)
for i in range(3, limit, 2):
if n % i == 0:
return False
return True

你看,通过一步步的思考和优化,判断一个数是不是 质数 的方法是不是越来越精妙了?这就是 算法 的魅力啊!从最笨拙的暴力法,到利用数学性质进行剪枝,代码虽然看着变化不大,但效率上的差距,那真是天壤之别。写代码不仅仅是把想法翻译成计算机语言,更重要的是思考怎么把事情做得又快又好,这才是编程真正的乐趣所在。

好,判断单个 质数 搞定了,那第二步,怎么找出一定范围内的所有 质数 呢?比如,找出1到10000之间的所有 质数

最直接的想法是什么?就是把前面判断单个 质数 的方法拿过来,从范围的起始点到结束点,一个一个地判断是不是 质数,如果是,就把它收集起来。

python
def find_primes_v1(limit):
primes = []
for n in range(2, limit + 1): # 从2开始到limit
if is_prime_v3(n): # 调用我们前面写好的判断函数
primes.append(n)
return primes

这个方法当然能找出所有 质数,但效率嘛… 如果limit很大,比如100万,那你就得对100万个数都做一次is_prime_v3的判断。虽然is_prime_v3已经优化过了,但累积起来的计算量还是相当可观的。有没有更快的方法呢?

有!而且非常经典,名字听起来有点酷炫—— 埃拉托斯特尼筛法(Sieve of Eratosthenes),简称 埃氏筛法

埃氏筛法 的思路特别巧妙。它不是一个一个地判断,而是一口气把所有非 质数 的数“筛”出去。想象一下,你有一堆数字,从2开始排队。你先找到第一个没被标记的数,它肯定是 质数(第一个是2)。然后,你把所有2的倍数(4、6、8、10…)都标记出来,告诉自己“这些数不是 质数 了”。接着,你再往后找,找到下一个没被标记的数(它是3)。3也是 质数!好,现在把所有3的倍数(6、9、12、15…)也都标记出来(如果6已经被2的倍数标记过了也没关系)。然后继续,下一个没被标记的是5,它是 质数!把所有5的倍数(10、15、20、25…)标记出来。就这样一直重复,直到你检查到范围的末尾,或者更准确地说,检查到范围上限的平方根。最后,所有没有被标记的数,就都是 质数

这个方法厉害在哪儿?它避免了重复的除法判断。你只需要遍历每个 质数 的倍数并标记,而不是对每个数都去尝试除法。这种“标记清除”的方式,比挨个做判断要快得多得多。

Python 怎么实现 埃氏筛法 呢?通常我们会用一个布尔类型的列表来表示每个数是不是 质数。列表的索引代表数字,列表的值是TrueFalseTrue表示可能是 质数(或尚未被标记为合数),False表示不是 质数

“`python
def sieve_of_eratosthenes(limit):
if limit < 2:
return [] # 小于2没有质数
# 创建一个布尔列表,默认所有数(从0到limit)都标记为True
# 列表大小是 limit + 1,索引 i 对应数字 i
is_prime = [True] * (limit + 1)
# 0和1不是质数,标记为False
is_prime[0] = is_prime[1] = False

# 从第一个质数2开始
# 只需要遍历到 limit 的平方根即可
# 因为如果一个合数 N 有大于 sqrt(N) 的因子,那它必然也有一个小于 sqrt(N) 的因子
# 而在遍历到这个小于 sqrt(N) 的因子时,这个合数 N 就会被标记为 False 了
for p in range(2, int(limit**0.5) + 1):
    # 如果 is_prime[p] 还是 True,说明 p 是一个质数
    if is_prime[p]:
        # 那就把 p 的所有倍数标记为 False
        # 从 p*p 开始标记,因为小于 p*p 的倍数(比如 2p, 3p 等)
        # 已经在 p 之前的小质数(2, 3等)的倍数筛选过程中被标记过了
        for i in range(p*p, limit + 1, p):
            is_prime[i] = False

# 最后遍历布尔列表,收集所有标记为 True 的索引(数字)
primes = [p for p in range(2, limit + 1) if is_prime[p]]
return primes

“`
这个 埃氏筛法算法 ,看起来是不是比前面那个暴力循环复杂一点?但它的效率提升是巨大的,尤其在找较大范围内的 质数 时,它的优势就完全显现出来了。这就是 算法 的力量啊!同一个问题,不同的 算法 解决起来,效果可能是天壤之别。

所以你看, python怎么弄质数 ?可以有很多种方法,从简单直接到巧妙高效。选择哪种方法,取决于你的需求。如果你只是想快速判断一两个小数字,用is_prime_v3那种优化过的判断函数就够了。但如果你的任务是要找出几万、几十万甚至更多范围内的所有 质数,那毫无疑问, 埃氏筛法 才是你的首选。

编程就是这样,一个问题抛给你,你可以有无数种解法。一开始,你可能只会最简单的那种,能解决问题就行。然后,你会慢慢学习更高效的 算法 和更优雅的代码写法,把事情做得更好。这个过程就像闯关升级,特别有成就感。 质数 只是个起点,数学里还有很多有趣的 算法 问题,都可以用 Python 来尝试解决。

别怕那些看起来有点吓人的数学名词,比如 埃拉托斯特尼筛法,它们背后往往藏着特别巧妙而且不复杂的思想。重要的是去理解那个思想,然后用你手里的 Python 工具,把它变成现实。动手去写,去调试,去体验代码跑起来的那种感觉。就算遇到 bug,也别灰心,那都是学习过程中的垫脚石。

总之, python怎么弄质数?这不仅仅是写几行代码的事儿,它包含着理解问题、思考 算法、优化方案、然后付诸实践的整个过程。掌握了这些,你就能 出更强大、更高效的程序,不仅仅是 质数,别的各种问题也能迎刃而解。去试试吧,你绝对能搞定!

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