嗨,各位码农朋友、或者只是对编程世界有点好奇的伙伴们,今天咱们聊点啥呢?就那个听起来带点数学味儿,但用 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
。这里头,a
和b
总有一个小于等于sqrt(n)
(sqrt
就是平方根)。为啥?要是a
和b
都大于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 怎么实现 埃氏筛法 呢?通常我们会用一个布尔类型的列表来表示每个数是不是 质数。列表的索引代表数字,列表的值是True
或False
,True
表示可能是 质数(或尚未被标记为合数),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怎么弄质数?这不仅仅是写几行代码的事儿,它包含着理解问题、思考 算法、优化方案、然后付诸实践的整个过程。掌握了这些,你就能 弄 出更强大、更高效的程序,不仅仅是 质数,别的各种问题也能迎刃而解。去试试吧,你绝对能搞定!
评论(0)