1 条题解

  • 0
    @ 2026-8-18 11:52:21

    思路

    素数(质数)是大于 1、且只能被 1 和它本身整除的数。所以 0、1 和所有负数都直接判 No。

    判断一个数 n 是不是素数,最直接的方法是试除法:用 2, 3, 4, … 逐个去试,看有没有数能整除 n。但不用试到 n,试到 √n 就够了:

    如果 n 有一个大于 √n 的因数 d,那么 n/d 一定是一个小于 √n 的因数——早就被试出来了。所以试到 √n 还没找到因数,n 一定是素数。

    多组输入的写法:while (scanf("%d", &x) != EOF),读到文件末尾自动停止。

    代码(正确版)

    #include <stdio.h>
    
    int isPrime(int n) {
        if (n < 2) return 0;          // 0、1、负数都不是素数
        for (int i = 2; i <= n / i; i++)   // 用除法代替 i*i<=n,避免溢出
            if (n % i == 0) return 0;
        return 1;
    }
    
    int main() {
        int x;
        while (scanf("%d", &x) != EOF) {
            if (isPrime(x)) printf("Yes\n");
            else            printf("No\n");
        }
        return 0;
    }
    

    关键坑:i * i <= n 会溢出!

    很多人(包括不少教材)把循环条件写成:

    for (int i = 2; i * i <= n; i++)
    

    在 n 不大时完全正确。但当 n 接近 int 上限(2147483647)且 n 是质数时,循环会一路走到 i = 46341,此时:

    • i * i = 46341 × 46341 = 2147488281,超过 int 最大值 2147483647;
    • 溢出后 i * i 变成负数(-2147479015),条件"负数 ≤ n"永远成立;
    • 而 n 是质数,n % i 永远不为 0,函数永远退不出循环 → 程序超时(TLE)。

    方法一(本代码采用):改用除法条件

    i <= n / i
    

    它与 i * i <= n 数学上等价(两边同乘 i 变形),但只有除法和比较,永远不会溢出。注意写成 i <= n / i 而不是 n / i >= i 之外的任何变形,含义相同。

    方法二:循环变量用 long long

    for (long long i = 2; i * i <= n; i++)
    

    long long 上限约 9.2×10¹⁸,i * i 最多 46341² ≈ 2.1×10⁹,绰绰有余。缺点是取模运算 n % i 混用了 int 和 long long,会有隐式类型转换(不影响正确性)。

    两种都行,方法一改动更小、也更锻炼对溢出的理解。

    也可以:为什么不用 sqrt(n)?

    for (int i = 2; i <= sqrt(n); i++)   // 不推荐
    

    sqrt 是浮点运算,对大数可能有微小误差(比如把 46340.9999 算成 46341.0 或反过来),导致循环次数差一、判断出错。如果一定要用,需写成 i <= (int)sqrt(n + 0.5) 来纠偏。既然 i <= n / i 又快又准,没必要冒浮点的险。

    样例演算

    输入 4:i = 2 时 4 % 2 == 0 → 返回 0 → 输出 No。

    输入 7:

    i i <= 7/i? 7 % i
    2 2 <= 3 ✓ 1
    3 3 <= 2 ✗ 退出 —

    循环结束没找到因数 → 返回 1 → 输出 Yes。

    易错点汇总

    • 小于 2 的数(0、1、负数)都不是素数,开头必须拦截,否则 1 会被误判为素数。
    • i * i <= n 在 n 接近 int 上限时溢出,导致质数判成死循环(详见上文);用 i <= n / i 或 long long。
    • 循环条件是"小于等于":i <= n / i,写错一个等号可能漏判完全平方数(如 n = 4 时 i = 2 恰好是 √n)。
    • 多组输入用 scanf(...) != EOF,别写死循环次数。
    • 1

    信息

    ID
    19
    时间
    1000ms
    内存
    256MiB
    难度
    2
    标签
    (无)
    递交数
    4
    已通过
    1
    上传者