# 23. 编写一个函数判断质数 def is_prime_number(num): if isinstance(num,int) and not isinstance(num,bool) and num>1: for i in range(3,num): #能被除了2和自身整除之外,还能被其他数整除就不是质数 if num%i==0: return "不是质数" else: return "是质数" else: return "不是是质数" def ask_int(prompt="请输入一个整数") -> int: while True: try: s=int(prompt) return s break except ValueError: print("请输入整数") continue if __name__=="__main__": num=ask_int() print(is_prime_number(num)) 请从一个优秀的大佬级别的专业程序员角度点评我这个程序。
ChatGPT 说:
燎然,这段代码思路是对的,但实现上有不少“坑”。我先毫不客气地挑错,然后给出一份“能上台面”的版本,再给你一点前瞻优化方向。保持实话实说+鼓励的风格:你离“能写出专业代码”只差几个习惯性的细节。
主要问题(按严重程度)
-
素数判断逻辑会“提前返回,永远只检查3”
for i in range(3, num): ... return ...放在循环里,一旦进循环就立刻return,导致只检查第一个i(=3),后面根本不走。 -
返回值语义不统一
is_prime_number返回中文字符串。判断函数最好返回bool,展示给用户的字符串由 UI/调用层负责。 -
遗漏/误判边界
-
2和3是质数; -
偶数(>2)必非质数;
-
1、0、负数、非整数、布尔值都不是质数(布尔是int的子类,True==1,False==0,需要特别处理)。
-
-
效率问题
质数判定只需试除到√n,不是到n-1。 -
类型校验方式不优雅
你写了isinstance(num, int) and not isinstance(num, bool),更通用的写法是isinstance(num, numbers.Integral) and not isinstance(num, bool);或者干脆在函数里把非整数直接返回False。 -
输入函数
ask_int有致命错误-
s = int(prompt)是把提示语转换成整数…永远抛异常或固定值; -
应该用
input(prompt)读取用户输入; -
return后面的break永远执行不到; -
建议支持去空格、重试,必要时提示再次输入。
-
一份专业可复用的实现
python复制编辑from math import isqrt
from numbers import Integral
def is_prime(n: int) -> bool:
"""返回 n 是否为质数(素数)。仅接受整数,其他类型返回 False。"""
# 类型与边界
if not isinstance(n, Integral) or isinstance(n, bool):
return False
if n < 2:
return False
# 快速通道
if n in (2, 3):
return True
if n % 2 == 0:
return False
# 试除到 sqrt(n),只测奇数
limit = isqrt(n)
for i in range(3, limit + 1, 2):
if n % i == 0:
return False
return True
def ask_int(prompt: str = "请输入一个整数:") -> int:
"""循环读取用户输入,直到给出合法整数。"""
while True:
s = input(prompt).strip()
try:
return int(s)
except ValueError:
print("❌ 非法输入,请输入整数。")
if __name__ == "__main__":
num = ask_int()
print("是质数" if is_prime(num) else "不是质数")
你原实现里每处该怎么改(对照式)
-
for i in range(3, num):→for i in range(3, isqrt(num) + 1, 2): -
循环内不要
else: return "是质数",应该在循环结束后再返回 True; -
增加前置判断:
if n in (2,3): return True,if n%2==0: return False; -
返回值改为
bool,输出层再转中文; -
ask_int用input(prompt)读取,再int()转换。
边界与自测(最少要过这些)
python复制编辑assert is_prime(2) is True
assert is_prime(3) is True
assert is_prime(4) is False
assert is_prime(1) is False
assert is_prime(0) is False
assert is_prime(-7) is False
assert is_prime(True) is False # 防止 bool 混入
assert is_prime(97) is True
assert is_prime(10**6 + 3) is False
进一步优化(前瞻性建议)
-
大整数判定:当
n很大(>1e12)时,试除也慢。可引入 Miller–Rabin(确定性底集)快速判定,速度量级差别巨大。 -
批量判定:如果要判定一堆数或生成质数表,用 埃拉托斯特尼筛(Sieve)。
-
API 设计:把 I/O 和计算解耦(现在就是这样做的),计算函数纯粹返回布尔值。
-
类型友好:若会接收
numpy.int64等,使用numbers.Integral更稳妥。 -
性能细节:可以先用小素数表(如 2,3,5,7,11,13,17,19,23,29)做快速排除,再进入主循环。
鼓励 vs 要求
你已经意识到了 bool 是 int 子类、需要排除,这说明你在思辨类型系统,这非常好。接下来,把“返回值语义清晰”“边界先行”“只循环到 √n”“I/O 与计算分离”这些习惯变成肌肉记忆,你写出来的代码自然会有“专业味道”。
要不要我给你补一版 Miller–Rabin 的实现,顺便写几组基准测试,让你直观看到性能差距?
您说:
质数判定只需试除到 √n ,√n 是什么? isinstance(num, numbers.Integral) 什么意思
ChatGPT 说:
好,我拆开给你讲,保证你彻底明白这两个概念。
1. 质数判定只需试除到 √n
原理
如果一个数 n 不是质数,它一定能分解成两个因数:
ini复制编辑n = a × b
那么:
-
如果 a > √n,那 b < √n
-
如果 b > √n,那 a < √n
也就是说,如果 n 有除 1 和 n 之外的因数,那这个因数中至少有一个 ≤ √n。
所以我们只需要检查 3 到 √n(取整),就能判定是否有因数,不需要一直检查到 n-1。
举例
假设 n = 49:
-
√49 = 7
-
如果 49 不是质数,那么它的因数要么 ≤ 7,要么 ≥ 7
(其实就是 7 × 7) -
所以我们只检查 2,3,4,5,6,7 就够了。
假设 n = 97:
-
√97 ≈ 9.84
-
检查 2,3,4,5,6,7,8,9
-
没找到能整除的数 → 它就是质数。
为什么快很多
比如 n=1,000,000:
-
直接试到 n-1 → 要试 999,998 次
-
只试到 √n ≈ 1,000 → 只试 1,000 次
差了将近 1000倍 的效率。
2. isinstance(num, numbers.Integral)
解释
isinstance(变量, 类型) 用来判断 变量是不是指定类型。
python复制编辑isinstance(10, int) # True
isinstance(3.14, int) # False
那 numbers.Integral 又是什么?
numbers 是 Python 的一个 数字类型抽象基类模块,Integral 是其中的一个类,表示 所有“整数类型”。
它包含:
-
Python 原生的
int -
还有 numpy 的
int64、int32等等
用 numbers.Integral 的好处是:
-
不仅能判断普通整数,还能判断其他库里的整数类型。
-
比单独写
isinstance(num, int)更通用。
例子
python复制编辑import numbers
import numpy as np
print(isinstance(10, numbers.Integral)) # True
print(isinstance(np.int64(10), numbers.Integral)) # True
print(isinstance(True, numbers.Integral)) # True (布尔是 int 子类)
print(isinstance(3.5, numbers.Integral)) # False
所以:
-
isinstance(num, numbers.Integral)判断是否是整数(包括 numpy 整数等) -
再加
and not isinstance(num, bool)排除布尔值这种特殊情况
我觉得你要是完全掌握了这个 √n 的原理和 numbers.Integral 的用途,你的质数判定函数就能写得又快又稳。
要不要我给你画一个质数试除到 √n 的流程图,让你一眼看懂它的工作过程?这样以后写算法的时候会超级清晰。
您说:
√ 这个怎么打出来呢? 我还是不懂isinstance(num, numbers.Integral) 和 isintance(num,int)的区别,再通俗点把
ChatGPT 说:
好,我帮你用超直白的方式解释,并且告诉你 √ 怎么打出来。
1. √ 怎么打出来
-
Windows 中文输入法
按Shift+^(数字 6 上面那个按键)没有用,它打不出来平方根。
正确方式是:-
中文输入法 → 输入
pingfanggen→ 选“平方根”符号 √ -
或者输入
sqrt,有些输入法也能选出来
-
-
Mac
直接Option+V= √ -
代码里
Python 打平方根不用符号,直接:python复制编辑import math math.sqrt(9) # 结果 3.0
2. isinstance(num, int) 和 isinstance(num, numbers.Integral) 的区别
我给你一个现实生活的类比:
int就像 “中国人” 这个定义(只能包含中国大陆的人)
numbers.Integral就像 “全世界所有说中文的人” 这个大类(包括大陆、台湾、香港、海外华侨等等)所以
numbers.Integral的范围更大,int是它里面的一个子集。
举例对比
python复制编辑import numbers
import numpy as np
# Python 自带 int
print(isinstance(10, int)) # True
print(isinstance(10, numbers.Integral)) # True
# numpy 的整数类型
print(isinstance(np.int64(10), int)) # False ❌
print(isinstance(np.int64(10), numbers.Integral)) # True ✅
# 布尔类型(True/False)
print(isinstance(True, int)) # True (布尔是 int 的子类,坑)
print(isinstance(True, numbers.Integral)) # True (一样的坑)
通俗总结
-
isinstance(num, int)→ 只认 Python 自带的 int -
isinstance(num, numbers.Integral)→ 认 所有整数类型,包括 Python int、numpy 的 int32/int64 等等 -
如果你的程序只会用到普通 Python 整数 → 用
int就够了 -
如果你的程序会处理 别的库里的整数类型 → 用
numbers.Integral更稳妥 -
布尔值坑:
True、False都会被认成整数,所以一般还要加and not isinstance(num, bool)过滤掉
我建议你先用 int,等以后做科学计算(用 numpy、pandas)再换成 numbers.Integral。
我可以帮你画一个两者范围的对比图,你看一眼就永远记住。
你要我画吗?这样你完全不会再混淆这两个。
您说:
import math def is_prime_number(num): if isinstance(num,int) and not isinstance(num,bool) and num>1: for i in range(2,int(math.sqrt(num))): #能被除了1和自身整除之外,还能被其他数整除就不是质数 if num%i==0 and i!=num: return False else: return True else: return False def ask_int(prompt="请输入一个整数") -> int: while True: try: s=int(input(prompt).strip()) return s break except ValueError: print("请输入整数") continue if __name__=="__main__": num=ask_int() print(is_prime_number(num)) 还是从专业的大佬级别的程序员角度点评我修改后的程序。
ChatGPT 说:
已思考 12s
燎然,这版比上次有进步,但还存在几个“致命点”。我按专业代码审查口吻给你点明问题→给出修正→再补上自测用例。
主要问题
-
循环里提前返回,仍然只检查第一个因子
python复制编辑if num % i == 0: return False else: return True # 这里一旦第一个 i 不能整除就直接 True 了正确做法:只有当所有 i 都不能整除时,循环结束后再
return True。 -
试除上限少了 1
range(2, int(math.sqrt(num)))是开区间上界,不会包含 √n。应该检查到⌊√n⌋,写法:range(2, int(math.sqrt(num)) + 1),更推荐math.isqrt(num)得到整数平方根,再+1。 -
边界数会返回
None
当num为 2 或 3 时,range(2, int(sqrt(num))+1)可能为空或只含 2,但你的函数里只有循环内才 return,循环不进/不命中就直接掉出函数,返回None。 -
无效判断
and i != num多余,i的取值范围不可能等于num。 -
性能改进空间
-
偶数快速排除:
num > 2 and num % 2 == 0 -> False -
之后只试奇数:
for i in range(3, isqrt(num)+1, 2)
-
-
ask_int的小问题
return s后面的break永远执行不到;可以去掉。
修正版(清爽、稳、快)
python复制编辑from math import isqrt
from numbers import Integral
def is_prime(num) -> bool:
"""判定 num 是否为质数。非整数或 <2 则 False。"""
if not isinstance(num, Integral) or isinstance(num, bool):
return False
if num < 2:
return False
if num in (2, 3):
return True
if num % 2 == 0:
return False
limit = isqrt(num)
for i in range(3, limit + 1, 2):
if num % i == 0:
return False
return True
def ask_int(prompt: str = "请输入一个整数:") -> int:
while True:
try:
return int(input(prompt).strip())
except ValueError:
print("请输入整数")
你这版对照怎么改
-
for i in range(2, int(math.sqrt(num))):→for i in range(3, isqrt(num) + 1, 2)并在此之前加上对2/3/偶数的快速判断。 -
去掉循环里的
else: return True,把return True放到循环结束后。 -
去掉
and i != num。 -
ask_int里删掉break。
最少要过的自测
python复制编辑assert is_prime(2) is True
assert is_prime(3) is True
assert is_prime(4) is False
assert is_prime(1) is False
assert is_prime(0) is False
assert is_prime(-7) is False
assert is_prime(True) is False # 防止布尔混入
assert is_prime(97) is True
assert is_prime(121) is False # 11*11,卡在 sqrt 处
前瞻建议
-
超大数判定:上 Miller–Rabin(确定性底集)做快速素性测试,速度会比试除快几个数量级。
-
批量需求:用埃拉托斯特尼筛生成区间素数表。
-
API 设计习惯:判定函数只返回
bool;展示层再把True/False转中文,解耦 I/O 与计算。
你现在最核心的一步就是“别在循环里太早 return”,以及“上界要包含 √n”。把这两个变成肌肉记忆,你写算法会稳很多。要不要我给你补一版 Miller–Rabin,让你直观看到性能差距?
您说:
import math def is_prime_number(num): if isinstance(num,int) and not isinstance(num,bool) and num>1: for i in range(2,int(math.sqrt(num))+1): #能被除了1和自身整除之外,还能被其他数整除就不是质数 if num%i==0: return False return True else: return False def ask_int(prompt="请输入一个整数") -> int: while True: try: s=int(input(prompt).strip()) return s except ValueError: print("请输入整数") continue if __name__=="__main__": num=ask_int() print(is_prime_number(num)) 看一下还有哪里错?
ChatGPT 说:
已思考 15s
燎然,这版已经能正确判定质数了,核心逻辑没问题👍。我从专业角度再挑下“还可以更稳更快”的点,并给你一版进阶实现。
还可以改进的点
-
性能细节
-
先做“快速排除”:
2、3直接返回True;>2的偶数直接False;num % 3 == 0直接False。 -
之后用 6k±1 轮子:从
i=5开始,只检查i和i+2(即 5、7、11、13…),每轮步长 6,减少约 1/3 检测次数。
-
-
整数平方根
- 用
math.isqrt(num)替代int(math.sqrt(num)),避免浮点误差且更快。
- 用
-
类型兼容
-
如果将来可能拿到
numpy.int64之类,isinstance(num, numbers.Integral)更通用。 -
但你现在用原生
int也没错。
-
-
函数命名与返回类型
- 判定函数建议返回
bool(你已经在用),名字简洁点:is_prime。
- 判定函数建议返回
-
输入函数小清洁
-
continue可省略; -
可以允许空输入时给友好提示;
-(可选)限制最大位数,防止用户误输入超大数导致久等。
-
进阶实现(稳+快)
python复制编辑from math import isqrt
from numbers import Integral
def is_prime(n) -> bool:
# 类型与边界
if not isinstance(n, Integral) or isinstance(n, bool):
return False
if n < 2:
return False
if n in (2, 3):
return True
if n % 2 == 0 or n % 3 == 0:
return False
# 6k ± 1 检测
limit = isqrt(n)
i = 5
while i <= limit:
if n % i == 0 or n % (i + 2) == 0:
return False
i += 6
return True
def ask_int(prompt="请输入一个整数:") -> int:
while True:
s = input(prompt).strip()
try:
return int(s)
except ValueError:
print("请输入整数")
最少自测(建议你跑一下)
python复制编辑assert is_prime(2) is True
assert is_prime(3) is True
assert is_prime(4) is False
assert is_prime(1) is False
assert is_prime(0) is False
assert is_prime(-7) is False
assert is_prime(True) is False
assert is_prime(97) is True
assert is_prime(121) is False # 11*11
小结
-
你这版逻辑已经正确✅;
-
换
isqrt、加快速排除+6k±1 后,同样正确但更快; -
记住两个肌肉记忆:上界包含 √n、不要在循环里过早 return True。
继续按这个标准写下去,你的代码很快就有“专业味儿”了。需要我给你补一版 Miller–Rabin(适合超大数)对比性能吗?
您说:
有没有 -> string: 这个写法?
ChatGPT 说:
已思考 31s
短答:没有,别写 -> string:。
在 Python 里应该写 -> str,不是 string。
再说人话版区别👇
-
str是 Python 内置的字符串类型。✅ -
string在 Python 里不是类型名(除非你自己定义了个名字叫string的东西)。❌
正确示例:
python复制编辑def ask_int(prompt: str = "请输入一个整数") -> int:
...
def to_text(x: int) -> str:
...
顺便补两个你会马上用到的注解写法(更“前瞻”):
python复制编辑# 3.10+ 联合类型(要么 str,要么 None)
def maybe_text(x: int) -> str | None:
...
# 3.9+ 容器类型(字符串列表)
def join_all(xs: list[str]) -> str:
return ",".join(xs)
版本小贴士(避免踩坑):
-
在 Python 3.11+,注解默认被当作字符串保存(延迟解析),
-> string不会立刻报错,但这只是把“错误”推迟了,类型检查器依然会提示你写错。 -
在 3.8~3.10,如果没有
from __future__ import annotations,-> string甚至会在函数定义时就因未定义名字报错。
结论:统一用 str。如果你以后会处理 numpy.int64 这类类型,也用对应的正规类型名(比如 numbers.Integral、np.integer 等),别随手写英文单词当类型。
您说:
6k ± 1 是啥
ChatGPT 说:
已思考 6s
简明说:6k ± 1 是一种质数试除的加速规律。
它是啥
对任意整数 n > 3,都能写成 6k + r 的形式,其中 r ∈ {0,1,2,3,4,5}。
-
r = 0, 2, 4→ 被 2 整除 -
r = 3→ 被 3 整除
所以想成为质数的候选,只可能是r = 1或r = 5,也就是 6k−1 或 6k+1。
这并不保证一定是质数,但其它形态必定不是,这样我们就能跳着检查,省下大量无用试除。
为什么能加速
普通试除:2,3,4,5,6,7,8,9,...
用 6k±1:只检查 5,7,11,13,17,19,23,25,29,31,...(成对出现:(6k−1, 6k+1))。
再配合“只试到 √n”,检查次数能明显下降。
代码怎么写
python复制编辑from math import isqrt
from numbers import Integral
def is_prime(n) -> bool:
if not isinstance(n, Integral) or isinstance(n, bool): return False
if n < 2: return False
if n in (2, 3): return True
if n % 2 == 0 or n % 3 == 0: return False
# 只检查 6k±1:即 i 和 i+2,i 每次加 6
limit = isqrt(n)
i = 5
while i <= limit:
if n % i == 0 or n % (i + 2) == 0:
return False
i += 6
return True
小例子
判断 n = 97:
-
先排掉偶数/3的倍数
-
√97 ≈ 9.8,只需测到 9
-
用 6k±1:只测
(5, 7);都不能整除 → 97 是质数
记住两句话
-
质数 > 3 一定属于 6k±1(只是候选,不是充分条件)。
-
实现时成对检查 (i, i+2),i 每次加 6,直到 √n。
您说:
已思考 6s 简明说:6k ± 1 是一种质数试除的加速规律。 它是啥 对任意整数 n > 3,都能写成 6k + r 的形式,其中 r ∈ {0,1,2,3,4,5}。 r = 0, 2, 4 → 被 2 整除 r = 3 → 被 3 整除 所以想成为质数的候选,只可能是 r = 1 或 r = 5,也就是 6k−1 或 6k+1。 这并不保证一定是质数,但其它形态必定不是,这样我们就能跳着检查,省下大量无用试除。 为什么能加速 普通试除:2,3,4,5,6,7,8,9,... 用 6k±1:只检查 5,7,11,13,17,19,23,25,29,31,...(成对出现:(6k−1, 6k+1))。 再配合“只试到 √n”,检查次数能明显下降。---再通俗点,一点都看不懂
ChatGPT 说:
已思考 15s
没问题,我用“幼儿园级别”的方式说清楚 6k±1 😂
一句话版
除了 2 和 3 以外,质数只可能长成这种样子:
6 的倍数前一位(6k−1)或者后一位(6k+1)。
比如:5、7、11、13、17、19、23、29…
(注意:只是候选,还要继续检查,比如 25=5×5 就不是质数。)
为啥是这样(超直白)
把自然数按 6 个一排摆开(因为 6 同时包含 2 和 3):
makefile复制编辑列: +1 +2 +3 +4 +5 +6(=0)
行1: 1 2 3 4 5 6
行2: 7 8 9 10 11 12
行3: 13 14 15 16 17 18
行4: 19 20 21 22 23 24
行5: 25 26 27 28 29 30
...
-
+2 列、+4 列、+6 列:都是偶数(被 2 整除)→ 不可能是质数
-
+3 列:都是 3 的倍数 → 不可能是质数
-
剩下只有 +1 列 和 +5 列(也就是 6k+1 和 6k−1)还有希望
你会看到:5、7、11、13、17、19、23、29 都落在 +1 或 +5 这两列。
但像 25(=5×5)、35(=5×7)、49(=7×7)也在这两列,但它们不是质数——所以“6k±1”只是把明显不可能的都先剔除,让你少做很多无用检查。
实战怎么用(写代码时的步骤)
- 先处理小情况:
-
n < 2不是质数 -
n in (2,3)是质数 -
n % 2 == 0 or n % 3 == 0直接不是质数
- 剩下的只检查形如 6k−1 和 6k+1 的因子:
-
从
i = 5开始,循环里每次检查i和i+2(也就是6k−1和6k+1),然后i += 6 -
只检查到
√n为止
这样就比“一个个数都试”快很多,也比“只试奇数”还要少大约 1/3 的检查量。
小例子(一看就懂)
判断 97:
-
不是偶数、不是 3 的倍数
-
√97 ≈ 9.8,只用检查到 9
-
用 6k±1:只检查 5 和 7 两个数
-
97 不能被 5、7 整除 → 97 是质数 ✅
就这么简单:把所有不可能的列(偶数列、3倍数列)先划掉,剩下两列再认真查。这样你脑子和电脑都省力。
您说:
判断 97: 不是偶数、不是 3 的倍数 √97 ≈ 9.8,只用检查到 9 用 6k±1:只检查 5 和 7 两个数 97 不能被 5、7 整除 → 97 是质数 ✅;用 6k±1:只检查 5 和 7 两个数 这里我不懂了,再通俗点
ChatGPT 说:
已思考 9s
很好,你卡住的点就两句话能说清:
为什么只检查 5 和 7?因为:
-
我们只需要检查 ≤ √97 的除数。√97 ≈ 9.8,取整就是 9,所以候选只到 9。
-
在 2
9 里,能成为质数因子的候选只能是 6k±1 这些数。把 2
9 列出来并筛一遍就明白了:
-
2(偶数,先排掉)
-
3(3 的倍数,排掉)
-
4(偶数,排掉)
-
5(=6×1−1,保留)
-
6(偶数 & 3 的倍数,排掉)
-
7(=6×1+1,保留)
-
8(偶数,排掉)
-
9(3 的倍数,排掉)
筛完只剩 5 和 7。再往后的 11、13 虽然也是 6k±1,但已经大于 9(超过 √97),按“只试到 √n”的原则根本不用看。
再用一句“防混淆”的话巩固记忆:
-
≤√n 的候选里,只有 6k−1、6k+1 两列值得查;
-
对 97 来说,≤9 的这两列只有 5 和 7 两个数,所以就查它俩;不能整除 → 97 是质数。
给你一个“三步口诀”,以后照背就行:
-
先排掉:
n<2、2/3特判,能被2或3整除的直接否。 -
设上限:
limit = ⌊√n⌋。 -
成对查:从
i=5开始,只查i和i+2(即 6k±1),每次i += 6,直到i > limit。
你把这套逻辑一想,就自然知道“为什么 97 只查 5 和 7”了。
您说:
import math #大于1的自然数、只能被1和自己整除 就是质数 def is_prime(num) ->bool: if num<2: # 把小于2的排除 return False if num==2 or num==3: # 把2,3 先处理掉 return True if num%2==0 or num%3==0: return True #剩下的就是6k+1和6k-1的数字了,只要检查int(math.sqrt(num)) 以下的整数就可以,这些整数的符合6k+-1 才有可能成为质数 balance=int(math.sqrt(num)) for i in range(4,balance) if (i-1)/6==0 or (i+1)/6==0 and num% if isinstance(num,int) and not isinstance(num,bool) and num>1: for i in range(2,int(math.sqrt(num))+1): #能被除了1和自身整除之外,还能被其他数整除就不是质数 if num%i==0: return False return True else: return False def ask_int(prompt="请输入一个整数") -> int: while True: try: s=int(input(prompt).strip()) return s except ValueError: print("请输入整数") if __name__=="__main__": num=ask_int() print(is_prime(num)) 我写不出来了,我的思路哪里错了?
ChatGPT 说:
已思考 22s
燎然,你这版卡住主要有 6 个点,逐条拆:
你思路里的关键错误
-
把能被 2 或 3 整除当成“质数”了
python复制编辑if num%2==0 or num%3==0: return True # ← 这里应该是 False -
6k±1 过滤写法错了
你写了(i-1)/6==0,这是“除法等于 0”,几乎只会在 i=1 成立;
正确应该是“模 6 的余数是 1 或 5”:i % 6 in (1, 5)。
不过更好的做法是根本不写这个判断,直接用“每次加 6,检查 i 和 i+2”。 -
for 语句少冒号、边界错
for i in range(4,balance)少了冒号,且上界需要包含√n:range(…, isqrt(num)+1)。 -
条件优先级
你写的(i-1)/6==0 or (i+1)/6==0 and num% ...,and、or优先级混在一起,极易出错。
如果真要合并条件,务必加括号:if (i % 6 in (1,5)) and (num % i == 0): ... -
重复两套逻辑
你先写了半套 6k±1,又复制了一份“从 2 到 √n”的旧逻辑,前者没 return,后者又覆盖了前者的意图,结构很乱。 -
用
math.sqrt不如math.isqrt
前者是浮点运算;isqrt直接给整数平方根,快且无精度问题。
给你两份“能直接跑”的版本
A) 保守稳定版(最小改动,先把功能写对)
python复制编辑import math
def is_prime(num) -> bool:
# 类型与边界
if not isinstance(num, int) or isinstance(num, bool) or num < 2:
return False
# 只试到 √n(包含)
limit = math.isqrt(num)
for i in range(2, limit + 1):
if num % i == 0:
return False
return True
B) 6k±1 加速版(专业写法)
python复制编辑from math import isqrt
def is_prime(num) -> bool:
if not isinstance(num, int) or isinstance(num, bool) or num < 2:
return False
if num in (2, 3):
return True
if num % 2 == 0 or num % 3 == 0:
return False
limit = isqrt(num)
i = 5
while i <= limit:
# 只查 6k-1 和 6k+1 两列:i 与 i+2
if num % i == 0 or num % (i + 2) == 0:
return False
i += 6
return True
注意:不要写成“先枚举所有 i,再用
i % 6 in (1,5)过滤”。那样既慢又啰嗦。直接用i=5起步,每次i+=6,检查i和i+2,最干净。
你的原思路如何修正(对照式)
-
if num%2==0 or num%3==0: return True→ 改成return False -
for i in range(4, balance)→ 改成for i in range(2, isqrt(num)+1): -
不要写
(i-1)/6==0这种检查,改用“i = 5; while i <= limit: 检 i 和 i+2; i += 6” -
删掉后面那段重复的“从 2 到 √n”的循环,保留一套清晰逻辑即可。
快速自测(至少过这些)
python复制编辑assert is_prime(2) is True
assert is_prime(3) is True
assert is_prime(4) is False
assert is_prime(1) is False
assert is_prime(0) is False
assert is_prime(-7) is False
assert is_prime(True) is False
assert is_prime(97) is True
assert is_prime(121) is False # 11*11
结论
你的大方向是对的:先特判、只试到 √n、想用 6k±1 加速。卡住点就是:
-
2/3 的可整除判断写反
-
6k±1 的实现方式不对
-
循环边界和结构混乱
把上面的修正版 copy 过去跑一遍,你就“过关”了。接下来我可以带你写 Miller–Rabin,让你亲眼看到大数(10^12 以上)质数判定的速度差,继续进阶。
您说:
math.isqrt 的作用是什么
ChatGPT 说:
已思考 14s
简短说:math.isqrt(n) 返回整数平方根,也就是 ⌊√n⌋(向下取整的平方根)。只接受非负整数,结果一定是 int,没有浮点误差。
为什么用它而不是 int(math.sqrt(n))?
-
isqrt不走浮点,大数也安全;math.sqrt需要把整数转成浮点,数一大就会溢出/精度丢失。 -
结果永远是正确的下取整;
int(math.sqrt(n))可能因浮点误差拿到错的边界。 -
速度和稳定性更好,适合你在质数判定里设置上界。
你要记住的用法:
python复制编辑from math import isqrt
n = 97
limit = isqrt(n) # 9
for i in range(2, limit + 1):
...
# 判断是否“完美平方数”
def is_perfect_square(x: int) -> bool:
if x < 0:
return False
r = isqrt(x)
return r * r == x
边界与异常:
-
isqrt(0) == 0,isqrt(1) == 1。 -
传负数会
ValueError。 -
传浮点会
TypeError(它就是给整型用的)。
一句话结论:写算法要算“到 √n”为止时,就用 math.isqrt(n),又准又快还不出幺蛾子。
您说:
for i<=limit: if num%i==0 or num%(i+2)==0: return True i+=6 return False
ChatGPT 说:
已思考 8s
你这段伪代码把逻辑反了、语法也不对。
核心原则:一旦找到能整除的数 → 不是质数 → 返回 False;全部检查完没找到 → 返回 True。
你写的:
python复制编辑for i<=limit:
if num%i==0 or num%(i+2)==0:
return True # ← 这里应该是 False
i+=6
return False # ← 这里应该是 True
正确的 6k±1 轮子写法(最小骨架):
python复制编辑i = 5
while i <= limit:
if num % i == 0 or num % (i + 2) == 0:
return False
i += 6
return True
放进完整函数(含边界与快排)就是:
python复制编辑from math import isqrt
def is_prime(n) -> bool:
if not isinstance(n, int) or isinstance(n, bool) or n < 2:
return False
if n in (2, 3):
return True
if n % 2 == 0 or n % 3 == 0:
return False
limit = isqrt(n)
i = 5
while i <= limit:
if n % i == 0 or n % (i + 2) == 0: # 只查 6k-1 和 6k+1
return False
i += 6
return True
再用 97 验证一遍思路:√97≈9,候选因子≤9 且形如 6k±1 的只有 5、7;5 和 7 都不能整除 → 返回 True。
顺手自测几条,确保你没有再反逻辑:
python复制编辑assert is_prime(2)
assert is_prime(3)
assert not is_prime(4)
assert is_prime(97)
assert not is_prime(121)
记住这句:找到因子就 False;找不到因子才 True。
您说:
limit=math.isqrt(num) i=5 for i<=limit: if num%i==0 or num%(i+2)==0: return False i+=6 return True 我这个limit下面怎么有红色的波浪线啊
ChatGPT 说:
已思考 26s
红波浪是语法错误:for i<=limit: 在 Python 里不合法。
for 只能写成 for 变量 in 可迭代对象:,不能跟条件;“条件循环”要用 while。
你这段要么改成 while,要么用 range 的 for。给你两种正确写法——任选一种:
写法 A:while(最常见的 6k±1 模板)
python复制编辑from math import isqrt
limit = isqrt(num)
i = 5
while i <= limit:
if num % i == 0 or num % (i + 2) == 0:
return False
i += 6
return True
写法 B:for + range(步长为 6)
python复制编辑from math import isqrt
limit = isqrt(num)
for i in range(5, limit + 1, 6):
if num % i == 0 or num % (i + 2) == 0:
return False
return True
再提醒三点,避免新的红线:
-
先
from math import isqrt或import math并用math.isqrt。 -
把这段放在函数里,并确保缩进统一(4 个空格,不要混用 Tab)。
-
别忘了前置快排逻辑:
python复制编辑if num < 2: return False if num in (2, 3): return True if num % 2 == 0 or num % 3 == 0: return False
总之:把 for i<=limit: 改成 while i <= limit:(或 for i in range(...))就没红线了。你思路没问题,卡的是语法。
您说:
import math #大于1的自然数、只能被1和自己整除 就是质数 def is_prime(num) ->bool: if num<2: # 把小于2的排除 return False if num==2 or num==3: # 把2,3 先处理掉 return True if num%2==0 or num%3==0: return False #剩下的就是6k+1和6k-1的数字了,只要检查int(math.sqrt(num)) 以下的整数就可以,这些整数的符合6k+-1 才有可能成为质数 limit=math.isqrt(num) i=5 while i<=limit: if num%i==0 or num%(i+2)==0: return False i+=6 return True def ask_int(prompt="请输入一个整数") -> int: while True: try: s=int(input(prompt).strip()) return s except ValueError: print("请输入整数") if __name__=="__main__": num=ask_int() print(is_prime(num)) --现在差不多对了吗?
ChatGPT 说:
已思考 10s
结论先说:这版是对的✅。逻辑正确、效率也上了台阶(√n 上界 + 6k±1 轮子)。
再给你几条专业级的小优化,能让它更稳、更通用:
-
类型安全(防止外部误传 float/np.int/布尔)
math.isqrt()只接受非负整数;万一别人传了3.0或True会出幺蛾子。建议加一行类型守卫:python复制编辑from numbers import Integral
def is_prime(num) -> bool: if not isinstance(num, Integral) or isinstance(num, bool): return False ...
-
isqrt 的导入与注释(更干净)
直接from math import isqrt,并把注释改成“检查到 √n(包含)”,避免“以下”歧义。 -
等价更简洁的写法(for + range)
功能一样,看着更规整一点:python复制编辑from math import isqrt from numbers import Integral
def is_prime(num) -> bool: if not isinstance(num, Integral) or isinstance(num, bool) or num < 2: return False if num in (2, 3): return True if num % 2 == 0 or num % 3 == 0: return False
limit = isqrt(num) for i in range(5, limit + 1, 6): # 6k-1 if num % i == 0 or num % (i + 2) == 0: # 6k+1 return False return True -
输入函数 OK
ask_int已经正确;如果你想更“人性化”,可以允许空输入重复提示,或限制位数避免大数卡住。 -
最少自测(跑一遍)
python复制编辑assert is_prime(2) assert is_prime(3) assert not is_prime(4) assert not is_prime(1) assert not is_prime(0) assert not is_prime(-7) assert not is_prime(True) # 防布尔混入 assert is_prime(97) assert not is_prime(121) # 11*11
想继续进阶的话,下一步上 Miller–Rabin(确定性底集)做大数素性测试,速度会比试除快几个数量级。你要的话我给你直接贴一版可用实现+基准对比。