易错点:循环临界条件的设置(素数版)

易错点:循环临界条件的设置(素数版)

标签:C语言 素数 循环 临界

by 小威威

1.反思

昨晚找了一道很简单的题练手,但没想到居然发现了自己的一个漏洞。对于素数,我通常用最普通的方法,就是判断数字N除以2~N-1是否出现整除情况。我知道有更好的算法,也懂它的原理,但就没有把它打一遍,因为我以为我会,我想如果有需要的话就再改进自己的代码。恰好昨晚用我那种最普通的方法题目总是超时,我便我按照我当时的模糊记忆改进了算法,果然出现了问题。虽然问题后来解决了,但是如果考试遇到这种情况了,岂不是浪费很多时间?所以告诫学生朋友们,每做到一道题,我们要了解较好的且用自己已有知识能够理解的那种方法并记忆下来,作为自己对于这类问题的常规解法。(不能像我一样把一种很渣的方法作为常规解法噢)与此同时,你还要记一种最牛的方法,试着打出来记住它的思路以备不时之需。

2.实例

  1. PRIME

Constraints

Time Limit: 1 secs, Memory Limit: 32 MB

Description

A prime number is a natural number which has exactly two distinct natural number divisors: 1 and itself. The first prime number is 2. Can you write a program that computes the nth prime number, given a number n <= 10000?

Input

The input contains just one number which is the number n as described above.
The maximum value of n is 10000.

Output

The output consists of a single line with an integer that is the nth prime number.

Sample Input

30
Sample Output

113

本题的大概意思是需要设计一个程序,用户输入数字N,则输出第N个素数。

# include <stdio.h>
# include <math.h>
int prime(int n);
int main(void) {
    int N, count = 1;
    scanf("%d", &N);
    if (1 == N) {
        printf("2\n");
        return 0;
    }
    for (int i = 3; ; i+=2) {
        if (1 == prime(i))
            count++;
        if (count == N) {
            printf("%d\n", i);
            break;
        }
    }
    return 0;
}
int prime(int n) {
    int i;    
    for (i = 2; i <= sqrt(n); i++) {
        if (n % i == 0) {
            break;
        }
    }
    if (i > sqrt(n)) return 1;
    return 0;
}

我主要要讲的还是循环临界条件的设置。这也是我本题的出错点。
起初我自函数的代码如下:

int prime(int n) {
    int i;    
    for (i = 2; i < sqrt(n); i++) {
        if (n % i == 0) {
            break;
        }
    }
    if (i >= sqrt(n)) return 1;
    return 0;
}

差别就在于for循环中循环条件没加等号和第二个if判断条件中加了等号。

我算法的改进在于:没有必要将n除以2~n-1,因为在2~n-1的范围内当数字大于根号n时,就不可能被n整除,所以干脆不检测大于根号n的那一段数字。那么,现在的问题是,在设置循环条件时,循环条件是否要加等号呢?

显然通过实践,发现两者结果不同,但问题其实不是由循环条件引起的,而是由第二个if引起的。因为无论循环条件有没有带等号,当循环结束时,i变量的值都是一样的。一般的数肯定一样,所以我们举个特殊的例子:比如开根为整数的9。在循环不加等号的条件下,它跳出循环时i == 3(正常退出,执行循环变量更新表达式后退出循环),在循环加等号的条件下,退出循环时i == 3(非正常退出,由break中止循环),所以说问题出在if。

if带等号时会将9包含在内,即将9当作素数(子函数中判断是素数返回1,不是素数返回0),所以会导致出错。

好,现在我们来分析一下结果:
前者的输出结果:30 113
后者的输出结果:30 103(而103是第27个素数)
后者程序多统计的是:9,25,49.
这三个数有什么特点呢?1.是奇数;2.除了1和本身,就只能被其自身开根整除。
那么为什么是这三个数呢?
首先,能满足i*i == n的数有4,9,16,25,36,49,64,81,100
由main函数我们知道,我们只检测奇数(因为偶数不可能是素数除了2),所以排除偶数,只剩下9,25,49,81
而且,i要达到临界状态,应满足“除了1和本身,就只能被其自身开根整除”这个条件。所以排除81。故多统计的数字是9,25,49。

3.总结

由此我们发现,循环临界条件也是一个能导致出错的地方,以后在这一条件设置的时候要仔细斟酌,避免发生类似的情况。

以上内容皆为本人观点,欢迎大家提出批评和指导意见,我们一起探讨。

你可能感兴趣的:(漏洞,C语言)