前言
大家好,这里是 Charming讲Python编码小技巧 系列专栏。每天分享一个 30-seconds-of-python 仓库中的神级写法,助你告别“屎山”代码,写出让人眼前一亮的 Pythonic 风格!
小技巧内容描述
质数(素数)是指只能被1和它本身整除的大于1的自然数。在编程面试和算法题中,判断质数是一个常见问题。今天教你一个高效且优雅的解决方案,短短几行代码就能搞定!
from math import sqrt
def is_prime(n):
if n <= 1 or (n % 2 == 0 and n > 2):
return False
return all(n % i for i in range(3, int(sqrt(n)) + 1, 2))
就这么简单!让我来详细解释一下为什么这么写。
为什么这么用?
核心原理:数学优化的魔力
这个技巧的核心在于利用数学性质进行优化,避免不必要的计算:
边界处理:
- n <= 1:小于等于1的数都不是质数
- n % 2 == 0 and n > 2:大于2的偶数都不是质数
关键优化:
- 只需要检查到 sqrt(n)(平方根)就够了!因为如果n有因子,至少有一个因子小于等于它的平方根
- 只检查奇数(从3开始,步长为2),跳过所有偶数
all()函数:
- all() 函数检查所有元素是否都为True
- 如果 n % i 都不为0,说明n不能被这些数整除,是质数
举个栗子
# 基础测试
print(is_prime(2)) # True (唯一的偶质数)
print(is_prime(3)) # True
print(is_prime(11)) # True
print(is_prime(17)) # True
print(is_prime(97)) # True
# 非质数
print(is_prime(1)) # False
print(is_prime(4)) # False
print(is_prime(15)) # False
print(is_prime(100)) # False
print(is_prime(121)) # False (11*11)
理解检查范围
以 is_prime(17) 为例:
- sqrt(17) ≈ 4.12
- range(3, 5, 2) = [3]
- 检查:17 % 3 = 2 ≠ 0
- all() 返回 True → 17是质数
以 is_prime(15) 为例:
- sqrt(15) ≈ 3.87
- range(3, 4, 2) = [3]
- 检查:15 % 3 = 0
- all() 返回 False → 15不是质数
更多实用场景
场景一:密码学应用 – 生成大质数
import random
def generate_large_prime(bits=64):
"""生成指定位数的大质数"""
while True:
# 生成一个奇数
n = random.getrandbits(bits) | 1
if is_prime(n):
return n
# 测试
large_prime = generate_large_prime(32)
print(f"生成的32位质数: {large_prime}")
print(f"验证: {is_prime(large_prime)}") # True
场景二:密码强度检测 – 质数密码奖励
def check_password_with_bonus(password):
"""检查密码,如果是质数长度给予奖励"""
password_len = len(password)
if password_len < 8:
print("密码长度不足8位")
return False
if is_prime(password_len):
print(f"密码长度 {password_len} 是质数!获得'数学达人'徽章!")
return True
else:
print(f"密码长度 {password_len} 位,注册成功")
return True
# 测试
check_password_with_bonus("abc12345") # 长度8 (非质数)
check_password_with_bonus("abc1234567") # 长度10 (非质数)
check_password_with_bonus("abc12345678") # 长度11 (质数!)
场景三:数据分页优化 – 质数页码
def optimize_page_numbers(total_items, items_per_page):
"""优化分页,优先显示质数页码"""
total_pages = (total_items + items_per_page – 1) // items_per_page
# 获取所有质数页码
prime_pages = [p for p in range(1, total_pages + 1) if is_prime(p)]
print(f"总页数: {total_pages}")
print(f"质数页码: {prime_pages}")
# 返回推荐页码(优先质数)
return prime_pages
# 测试
optimize_page_numbers(100, 10) # 10页,质数页: [2, 3, 5, 7]
场景四:找质数对 – 哥德巴赫猜想验证
def find_prime_pairs(n):
"""找出所有两个质数之和等于n的组合"""
pairs = []
for i in range(2, n // 2 + 1):
if is_prime(i) and is_prime(n – i):
pairs.append((i, n – i))
return pairs
# 验证哥德巴赫猜想(每个大于2的偶数可以表示为两个质数之和)
for even_num in [10, 14, 20, 28, 100]:
pairs = find_prime_pairs(even_num)
print(f"{even_num} = {pairs}")
性能对比
让我们看看这个方法和其他方法相比性能如何:
import time
# 方法1:使用sqrt优化(推荐)
def method1(n):
if n <= 1 or (n % 2 == 0 and n > 2):
return False
return all(n % i for i in range(3, int(sqrt(n)) + 1, 2))
# 方法2:暴力检查所有数
def method2(n):
if n <= 1:
return False
for i in range(2, n):
if n % i == 0:
return False
return True
# 方法3:检查到n,但不使用all()
def method3(n):
if n <= 1:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True
# 性能测试
test_number = 9999991 # 一个大质数
# 测试方法1
start = time.time()
result1 = method1(test_number)
print(f"方法1(sqrt优化): {time.time() – start:.6f}秒, 结果: {result1}")
# 测试方法2
start = time.time()
result2 = method2(test_number)
print(f"方法2(暴力): {time.time() – start:.6f}秒, 结果: {result2}")
# 测试方法3
start = time.time()
result3 = method3(test_number)
print(f"方法3(循环): {time.time() – start:.6f}秒, 结果: {result3}")
结果对比:
- 方法1(sqrt优化):最快,简洁优雅
- 方法2(暴力):最慢,对于大数几乎不可用
- 方法3(循环):次快,但代码较长
进阶技巧:找出指定范围内的所有质数
方法1:简单遍历
def find_primes_in_range(start, end):
"""找出指定范围内的所有质数"""
return [n for n in range(start, end + 1) if is_prime(n)]
# 找出1到100的所有质数
primes = find_primes_in_range(1, 100)
print(f"1到100的质数: {primes}")
print(f"共 {len(primes)} 个")
方法2:埃拉托斯特尼筛法(更高效)
def sieve_of_eratosthenes(n):
"""埃拉托斯特尼筛法找出小于等于n的所有质数"""
if n < 2:
return []
sieve = [True] * (n + 1)
sieve[0] = sieve[1] = False
for i in range(2, int(n ** 0.5) + 1):
if sieve[i]:
sieve[i*i : n+1 : i] = [False] * len(sieve[i*i : n+1 : i])
return [i for i, is_prime in enumerate(sieve) if is_prime]
# 测试
primes = sieve_of_eratosthenes(100)
print(f"1到100的质数: {primes}")
print(f"共 {len(primes)} 个")
# 性能对比
import time
n = 1000000
start = time.time()
primes1 = find_primes_in_range(1, n)
print(f"简单遍历法: {time.time() – start:.4f}秒")
start = time.time()
primes2 = sieve_of_eratosthenes(n)
print(f"筛法: {time.time() – start:.4f}秒")
质数趣味知识
总结
这个"判断质数"的技巧之所以如此优雅,是因为:
下次遇到需要判断质数的场景,不妨试试这个技巧,让你的代码既简洁又高效!
今日小技巧: all(n % i for i in range(3, int(sqrt(n)) + 1, 2)) – 高效判断质数
适用场景: 密码学、算法题、数据验证、数学计算等
点赞收藏,每天学一个Python神技巧!



