【问题标题】:Prime Number between given interval给定区间之间的质数
【发布时间】:2021-11-02 10:33:17
【问题描述】:

下面给出的是在用户输入的区间中查找素数的代码。

#include <stdio.h>

int main() {
    int n1, n2, i, flag;
    scanf("%d%d", &n1, &n2);
    for (i = n1; i <= n2; i++) {
        flag = prime(i);
        
        if (flag == 1)
            printf("\n%d", i);
        
    }
    return 0;
}

int prime(int n) {
    int j, flag = 1;
    for (j = 2; j <= n / 2; j++) {
        if (n % j == 0) {
            flag = 0;
            break;
        }
    }
    return flag;
}

谁能解释一下这段代码如何处理非素数的奇数(例如:15、21、25 等)

int prime(int n) {
    int j, flag = 1;
    for (j = 2; j <= n / 2; j++) {
        if (n % j == 0) {
            flag = 0;
            break;
        }
    }
    return flag;
}

看这个prime函数,当我们观察for循环的迭代时如果n的值为15那么它会是这样的:

for (j = 2; j <= 15 / 2; j++)  

我同意这是真的。 因为 2。 由于条件为真,我们将进入 for 循环:

    if(n%j==0){
        flag=0;
        break;
    }

现在,由于n=15,j=2,n%j=1的值,显然不等于0;所以 if 循环不会被执行,prime 函数将返回 flag =1;并且 main 函数将打印 15 作为素数。

但是,在执行程序后,代码显示了正确的结果:它没有将 15 显示为素数。

那么任何人都可以帮助我理解这段代码背后的逻辑吗? (其实我想了解这段代码是如何消除非质数奇数的。)

【问题讨论】:

  • 递归意味着一个函数调用自己。这显然不是这里的情况。为什么在标题和标签中提到这一点?
  • 该程序不会消除非素数奇数...它正在处理大于2的所有奇数和偶数,直到n / 2。它在哪里说它正在消除非质数奇数。)你从哪里得到的?您可以从循环中消除所有 偶数,因为只有一个您必须处理 (2),但必须考虑赔率。请参阅我对这个问题的回答。我在回答中这样做了。

标签: c function loops for-loop


【解决方案1】:

您检查了j==2 的执行情况,但因为有一个for 循环for(j=2;j&lt;=n/2;j++)。代码将从 j=2 运行到 j=n/2。所以,如果你考虑所有的迭代,你会发现这个函数运行良好。

  1. 第一个if 语句为假,因此对于 j==2,程序不会进入 if 语句。
  2. 循环将迭代 j 的下一个值,即 3。由于 15%3 == 0,程序将执行 if 语句中的语句并返回 15 不是质数。
    for(j=2;j<=n/2;j++){
        if(n%j==0){
            flag=0;
            break;
        }
    }

【讨论】:

    【解决方案2】:

    对于n=15,循环从i=2开始,测试i&lt;=n/2为真,因为2&lt;=7,那么15%21,因此循环继续 并且i 递增到3,循环测试再次为真,因为3&lt;=715%30,所以flag 设置为0 并返回。

    请注意以下备注:

    • 代码没有递归函数。您只需调用函数prime() 来检查区间中的每个数字的素数。
    • prime() 应该在调用它的main() 函数之前定义或至少声明。
    • 可以直接测试prime(i)的返回值。不需要flag 变量。
    • 对于素数,循环将迭代得太远:您可以将测试更改为 j &lt;= n / j 以在 n 的平方根处停止。
    • 您可以直接从循环体返回。
    • 你应该在数字之后输出换行符

    这是修改后的版本:

    #include <stdio.h>
    
    int isprime(int n) {
        int j;
        for (j = 2; j <= n / j; j++) {
            if (n % j == 0)
                return 0;
        }
        return 1;
    }
    
    int main() {
        int n1, n2, i;
    
        if (scanf("%d%d", &n1, &n2) != 2)
            return 1;
        for (i = n1; i <= n2; i++) {
            if (isprime(i))
                printf("%d\n", i);
        }
        return 0;
    }
    

    【讨论】:

    • 我以前从未见过n / j 接近先生。您能否分享它的解释性链接或来源,了解它是如何工作的?
    • @snr 好吧,如果你想停在n 的平方根,而不是j &lt;= sqrt(n),你可以写j * j &lt;= n,但是在使用@ 时乘法可能(最终)溢出987654347@ 编译器还可以在the same time 处执行除法和连续模运算 (n % j)。
    • 这是如何工作的if (scanf("%d%d", &amp;n1, &amp;n2) != 2) 我从未在 if 语句中看到 scanf 你能解释一下它是如何工作的吗?为什么您在扫描后将输入与 2 进行比较?
    【解决方案3】:

    谁能解释一下这段代码如何处理非素数的奇数(例如:15、21、25 等)

    int prime(int n) {
        int j, flag = 1;
        for (j = 2; j <= n / 2; j++) {
            if (n % j == 0) {
                flag = 0;
                break;
            }
        }
        return flag;
    }
    

    这个函数不需要特别处理非质数,基于这样一个事实,如果我们可以将数字n 除以某个东西(是否为质数),则该数字将被组合。一旦找到一个除以n 的数字j,它如何摆脱循环(将标志更改为0)。

    还有一个额外的优化,可以为您节省大量时间,它包括计算数字,直到整数向下舍入 n 的平方根,如果您可以将该数字除以大于平方的数字根,肯定会有一个小于平方根的数字也除以n(原始数字除以第一个的结果会给你一个小于平方根的数字)所以你只需要向上直到平方根。虽然计算平方根可能很乏味(有一个库函数,但让我们继续),它只完成一次,所以使用它是一个好点。此外,您可以先尝试将数字除以 2,然后跳过所有偶数,方法是将 2 加到 j,而不是递增。

    #include <math.h>
    
    /* ... */
    
    int prime(unsigned n) {
        /* check for special cases */
        if (n >= 1 && n <= 3) return TRUE; /* all these numbers are prime */
        if (n % 2 == 0) return FALSE; /* all these numbers are not */
        /* calculate (only once) the rounded down integer square root */
        int j, square_root = isqrt(n); /* see below */
        for (j = 3; j <= square_root; j += 2) { /* go two by two */
            if (n % j == 0)
                return FALSE;
        }
        /* if we reach here, all tests failed, so the number must be prime */
        return TRUE;
    }
    

    虽然&lt;math.h&gt; 中有一个sqrt() 函数,但我建议您编写平方根例程的整数版本(您可以轻松设计它),因此您不需要完全精确地计算它(只是为了整数精度)。

    /* the idea of this algorithm is that we have two numbers between 1 and n,
     * the greater being the arithmetic mean between the previous two, while
     * the lower is the result of dividing the original n by the arithmetic mean.
     * it is sure than if we select the arithmetic mean, the number will be
     * between the previous ones, and if I divide n by a number that is lower,
     * the quotient will be higher than the original number.  By the way, the
     * arithmetic mean is always bigger than the square root, so the quotient
     * will be smaller.  At each step, both numbers are closer to each other, and
     * so, the smaller is closer to the result of dividing n by itself (and this
     * is the square root!)
     */
    unsigned isqrt(unsigned n)
    {
        unsigned geom = 1, arith = n;
        while (geom < arith) {
            arith = (geom + arith) / 2;
            geom = n / arith;
        }
        /* return the smaller of the two */
        return arith;
    }
    

    所以,您的程序将是:

    #include <stdio.h>
    
    #define FALSE (0)
    #define TRUE (!FALSE)
    
    unsigned isqrt(unsigned n)
    {
        unsigned geom = 1, arith = n;
        while (geom < arith) {
            arith = (geom + arith) / 2;
            geom = n / arith;
        }
        return arith;
    }
    
    int prime(unsigned n) {
        /* check for special cases */
        if (n >= 1 && n <= 3) return TRUE;
        if (n % 2 == 0) return FALSE;
        /* calculate (only once) the rounded down integer square root */
        int j, square_root = isqrt(n);
        for (j = 3; j <= square_root; j += 2) {
            if (n % j == 0) {
                return FALSE;
            }
        }
        return TRUE;
    }
    
    int main() {
        unsigned n1, n2, i;
    
        scanf("%u%u", &n1, &n2);
    
        for (i = n1; i <= n2; i++) {
            if (prime(i))
                printf("%u\n", i);
    
        }
        return 0;
    }
    

    如果您尝试使用您的版本来对抗这个值,使用 20000000002000000100 之类的值,您将看到这是如何节省大量计算的(实际上,对于以下情况,仅考虑奇数时的情况通过循环将取出一半的数字---这是 1000000000 个测试---,但平方根会将测试数量减少到它的平方根---只有大约 40000 个测试--- 每个数字!!!)。

    $ primes
    2000000000 2000000100 
    2000000011
    2000000033
    2000000063
    2000000087
    2000000089
    2000000099
    $ _
    

    你的版本需要(在我的系统上)这个执行时间:

    $ echo 2000000000 2000100000 | time primes0 >/dev/null
    3.09user 0.00system 0:03.09elapsed 99%CPU (0avgtext+0avgdata 1468maxresident)k
    0inputs+0outputs (0major+69minor)pagefaults 0swaps
    $ _
    

    而建议的版本需要:

    $ echo 2000000000 2000100000 | time primes >/dev/null
    0.78user 0.00system 0:00.78elapsed 99%CPU (0avgtext+0avgdata 1572maxresident)k
    0inputs+0outputs (0major+72minor)pagefaults 0swaps
    $ _
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-08
      • 1970-01-01
      • 2022-12-02
      • 2012-08-06
      • 1970-01-01
      • 2013-06-11
      • 1970-01-01
      相关资源
      最近更新 更多