谁能解释一下这段代码如何处理非素数的奇数(例如: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;
}
虽然<math.h> 中有一个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;
}
如果您尝试使用您的版本来对抗这个值,使用 2000000000 和 2000000100 之类的值,您将看到这是如何节省大量计算的(实际上,对于以下情况,仅考虑奇数时的情况通过循环将取出一半的数字---这是 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
$ _