Python 编写一个函数判断质数

92 阅读17分钟

# 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 说:

燎然,这段代码思路是对的,但实现上有不少“坑”。我先毫不客气地挑错,然后给出一份“能上台面”的版本,再给你一点前瞻优化方向。保持实话实说+鼓励的风格:你离“能写出专业代码”只差几个习惯性的细节。

主要问题(按严重程度)

  1. 素数判断逻辑会“提前返回,永远只检查3”
    for i in range(3, num): ... return ... 放在循环里,一旦进循环就立刻 return,导致只检查第一个 i(=3),后面根本不走。

  2. 返回值语义不统一
    is_prime_number 返回中文字符串。判断函数最好返回 bool,展示给用户的字符串由 UI/调用层负责。

  3. 遗漏/误判边界

    • 23 是质数;

    • 偶数(>2)必非质数;

    • 1、0、负数、非整数、布尔值 都不是质数(布尔是 int 的子类,True==1False==0,需要特别处理)。

  4. 效率问题
    质数判定只需试除到 √n,不是到 n-1

  5. 类型校验方式不优雅
    你写了 isinstance(num, int) and not isinstance(num, bool),更通用的写法是 isinstance(num, numbers.Integral) and not isinstance(num, bool);或者干脆在函数里把非整数直接返回 False

  6. 输入函数 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 Trueif n%2==0: return False

  • 返回值改为 bool,输出层再转中文;

  • ask_intinput(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 要求

你已经意识到了 boolint 子类、需要排除,这说明你在思辨类型系统,这非常好。接下来,把“返回值语义清晰”“边界先行”“只循环到 √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 的 int64int32 等等

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 更稳妥

  • 布尔值坑TrueFalse 都会被认成整数,所以一般还要加 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

燎然,这版比上次有进步,但还存在几个“致命点”。我按专业代码审查口吻给你点明问题→给出修正→再补上自测用例。

主要问题

  1. 循环里提前返回,仍然只检查第一个因子

    python复制编辑if num % i == 0: return False
    else: return True   # 这里一旦第一个 i 不能整除就直接 True 了
    

    正确做法:只有当所有 i 都不能整除时,循环结束后再 return True

  2. 试除上限少了 1
    range(2, int(math.sqrt(num))) 是开区间上界,不会包含 √n。应该检查到 ⌊√n⌋,写法:range(2, int(math.sqrt(num)) + 1),更推荐 math.isqrt(num) 得到整数平方根,再 +1

  3. 边界数会返回 None
    num 为 2 或 3 时,range(2, int(sqrt(num))+1) 可能为空或只含 2,但你的函数里只有循环内才 return,循环不进/不命中就直接掉出函数,返回 None

  4. 无效判断
    and i != num 多余,i 的取值范围不可能等于 num

  5. 性能改进空间

    • 偶数快速排除:num > 2 and num % 2 == 0 -> False

    • 之后只试奇数:for i in range(3, isqrt(num)+1, 2)

  6. 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

燎然,这版已经能正确判定质数了,核心逻辑没问题👍。我从专业角度再挑下“还可以更稳更快”的点,并给你一版进阶实现。

还可以改进的点

  1. 性能细节

    • 先做“快速排除”:2、3 直接返回 True>2 的偶数直接 Falsenum % 3 == 0 直接 False

    • 之后用 6k±1 轮子:从 i=5 开始,只检查 ii+2(即 5、7、11、13…),每轮步长 6,减少约 1/3 检测次数。

  2. 整数平方根

    • math.isqrt(num) 替代 int(math.sqrt(num)),避免浮点误差且更快。
  3. 类型兼容

    • 如果将来可能拿到 numpy.int64 之类,isinstance(num, numbers.Integral) 更通用。

    • 但你现在用原生 int 也没错。

  4. 函数命名与返回类型

    • 判定函数建议返回 bool(你已经在用),名字简洁点:is_prime
  5. 输入函数小清洁

    • 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.Integralnp.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 = 1r = 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+16k−1)还有希望

你会看到:5、7、11、13、17、19、23、29 都落在 +1 或 +5 这两列。
但像 25(=5×5)、35(=5×7)、49(=7×7)也在这两列,但它们不是质数——所以“6k±1”只是把明显不可能的都先剔除,让你少做很多无用检查。

实战怎么用(写代码时的步骤)

  1. 先处理小情况:
  • n < 2 不是质数

  • n in (2,3) 是质数

  • n % 2 == 0 or n % 3 == 0 直接不是质数

  1. 剩下的只检查形如 6k−16k+1 的因子:
  • i = 5 开始,循环里每次检查 ii+2(也就是 6k−16k+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?因为:

  1. 我们只需要检查 ≤ √97 的除数。√97 ≈ 9.8,取整就是 9,所以候选只到 9

  2. 在 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 是质数。

给你一个“三步口诀”,以后照背就行:

  1. 先排掉:n<22/3 特判,能被 23 整除的直接否。

  2. 设上限:limit = ⌊√n⌋

  3. 成对查:从 i=5 开始,只查 ii+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 个点,逐条拆:

你思路里的关键错误

  1. 把能被 2 或 3 整除当成“质数”了

    python复制编辑if num%2==0 or num%3==0:
        return True   # ← 这里应该是 False
    
  2. 6k±1 过滤写法错了
    你写了 (i-1)/6==0,这是“除法等于 0”,几乎只会在 i=1 成立;
    正确应该是“模 6 的余数是 1 或 5”:i % 6 in (1, 5)
    不过更好的做法是根本不写这个判断,直接用“每次加 6,检查 i 和 i+2”。

  3. for 语句少冒号、边界错
    for i in range(4,balance) 少了冒号,且上界需要包含 √nrange(…, isqrt(num)+1)

  4. 条件优先级
    你写的 (i-1)/6==0 or (i+1)/6==0 and num% ...andor 优先级混在一起,极易出错。
    如果真要合并条件,务必加括号:if (i % 6 in (1,5)) and (num % i == 0): ...

  5. 重复两套逻辑
    你先写了半套 6k±1,又复制了一份“从 2 到 √n”的旧逻辑,前者没 return,后者又覆盖了前者的意图,结构很乱。

  6. 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,检查 ii+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) == 0isqrt(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,要么用 rangefor。给你两种正确写法——任选一种:

写法 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

再提醒三点,避免新的红线:

  1. from math import isqrtimport math 并用 math.isqrt

  2. 把这段放在函数里,并确保缩进统一(4 个空格,不要混用 Tab)。

  3. 别忘了前置快排逻辑:

    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 轮子)。

再给你几条专业级的小优化,能让它更稳、更通用:

  1. 类型安全(防止外部误传 float/np.int/布尔)
    math.isqrt() 只接受非负整数;万一别人传了 3.0True 会出幺蛾子。建议加一行类型守卫:

    python复制编辑from numbers import Integral

    def is_prime(num) -> bool: if not isinstance(num, Integral) or isinstance(num, bool): return False ...

  2. isqrt 的导入与注释(更干净)
    直接 from math import isqrt,并把注释改成“检查到 √n(包含)”,避免“以下”歧义。

  3. 等价更简洁的写法(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
    
  4. 输入函数 OK
    ask_int 已经正确;如果你想更“人性化”,可以允许空输入重复提示,或限制位数避免大数卡住。

  5. 最少自测(跑一遍)

    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(确定性底集)做大数素性测试,速度会比试除快几个数量级。你要的话我给你直接贴一版可用实现+基准对比。