C语言 | 六种方法输出100以内的素数
目录
写在前面:
输出前20万个素数,对比简单遍历和欧拉筛选的运行时间。
简单遍历:
#include <stdio.h>
#include <time.h>
clock_t start, stop;
double duration;
int main()
{
int count = 0;
start = clock(); /*开始计时*/
int i, j;
for (i = 2; i <= 200000; i++) {
int isprime = 1;
for (j = 2; j < i; j++) {
if (i % j == 0) {
isprime = 0;
break;
}
}
if (isprime == 1) {
printf("%d\n", i);
count++;
}
}
printf("%d\n", count);
stop = clock(); /*停止计时*/
duration = ((double)(stop - start)) / CLK_TCK; /*计算运行时间*/
printf("Running time: %lf seconds\n", duration); // 输出运行时间
return 0;
}
3.243秒
欧拉筛选:
#include <stdio.h>
#include <stdbool.h>
#include <time.h>
clock_t start, stop;
double duration;
int main()
{
int n = 200000;
bool isPrime[200001];
int prime[200001];
int cnt = 0; //记录素数的个数
for (int i = 2; i <= n; i++)
isPrime[i] = true;
for (int i = 2; i <= n; i++)
{
if (isPrime[i])
{
prime[cnt] = i; //将i加入素数数组
cnt++;
}
for (int j = 0; j < cnt && i * prime[j] <= n; j++)
{
isPrime[i * prime[j]] = false; //将当前数与质因数的积标记为合数
if (i % prime[j] == 0)
break; //保证每个合数只会被它的最小质因数筛选一次
}
}
printf("素数:\n");
for (int i = 0; i < cnt; i++)
{
printf("%d\n", prime[i]);
}
printf("共有素数%d个\n", cnt);
stop = clock(); /*停止计时*/
duration = ((double)(stop - start)) / CLOCKS_PER_SEC; /*计算运行时间*/
printf("Running time: %lf seconds\n", duration); // 输出运行时间
return 0;
}
0.353秒
一、简单遍历
这是一个简单的C语言程序,实现的功能是打印出2到100之间的所有素数。
程序的基本思路是:用变量i从2开始逐个遍历到100,对于每一个i,用变量j从2开始逐个遍历到i-1,如果i能被j整除,则说明i不是素数,将isPrime标志位设为0,然后跳出循环。如果在循环结束后isPrime仍然是1,则说明i是素数,将其打印出来即可。
#include <stdio.h>
int main() {
int i, j;
for (i = 2; i <= 100; i++) {
int isPrime = 1;
for (j = 2; j < i; j++) {
if (i % j == 0) {
isPrime = 0;
break;
}
}
if (isPrime == 1) {
printf("%d\n", i);
}
}
return 0;
}
二、遍历至该数的平方根
程序的基本思路是:定义一个isprime函数,用来判断一个数是否为素数。isprime函数的实现方式与之前的程序相同,用一个循环遍历到该数的平方根即可。
在主函数中,用for循环逐个枚举2到n之间的数字,如果这个数字是素数就打印出来,并将cnt计数器自增。最后输出素数的总个数。
相较于之前的程序,这个程序代码更加简洁,可读性也更好,而且使用了一个新的函数来判断素数,使得程序更加模块化,代码复用性更高。
#include<stdio.h>
#include<math.h>
bool isprime(int x)
{
for (int i = 2; i <=sqrt(x); i++)
{
if (x % i == 0) return 0;
}
return 1;
}
int main()
{
int n, cnt = 0;
scanf("%d", &n);
for (int i = 2; i <= n; i++)
{
if (isprime(i))
{
cnt++;
printf("%d\n", i);
}
}
return 0;
}
三、用x/i来代替sqrt(x)
对于i<=sqrt(x) sqrt函数的计算会比较慢,因此我们两边平方后,移一个i过去右边,变成i<=x/i 这样就能避免溢出的问题 。程序的代码基本与之前的程序相同,只是在函数isprime中的循环控制条件做了一下优化,用x/i来代替sqrt(x),达到减少循环次数的效果。
#include<stdio.h>
bool isprime(int x)
{
for (int i = 2; i <= x / i; i++)
{
if (x % i == 0) return 0;
}
return 1;
}
int main()
{
int n,cnt=0;
double t1 = clock();
scanf("%d", &n);
for (int i = 2; i <= n; i++)
{
if (isprime(i))
{
cnt++;
printf("%d\n", i);
}
}
return 0;
}
四、朴素筛法
一个合数一定存在非1非本身的质因子。
程序的基本思路是:首先定义一个长度为n的数组pri,用来记录每一个数字是否是素数。然后用for循环遍历到n的平方根,如果i是素数,则用一个内部循环依次去除i的倍数,将这些数标记为合数。最后再用for循环遍历整个数组,将所有没有被标记过的数字输出出来。
这个算法的优点在于避免了之前程序内部判断素数时的重复计算,同时减少了程序的循环次数。缺点是需要用一个数组来存储中间状态,可能会消耗更多的内存。
#include<stdio.h>
//0为素数,1为合数
int pri[100] = {0};
int main()
{
int n,cnt=0;
double t1 = clock();
scanf("%d", &n);
for (int i = 2; i <= n/i; i++)
{
if (!pri[i])//没被筛过
{
for (int j = 2 * i; j <= n; j += i)//去除合数
{
pri[j] = 1;
}
}
}
for (int i = 2; i <= n; i++)
{
if (!pri[i])
{
cnt++;
printf("%d\n", i);
}
}
return 0;
}
五、埃式筛法
该程序是朴素筛法程序的一个修正,主要在内部循环的控制条件上做了改变。
由于在之前的程序中,内部循环是从2 * i开始依次将i的倍数标记为合数。但在这个程序中,内部循环是从i * i开始依次将i的倍数标记为合数。这是因为在i * i之前,这些数字已经被其他的素数筛选过了,所以不需要再次去除。
这个算法的优点和上一个程序的优点一样,避免了之前程序的重复计算,减少了程序的循环次数。同时由于内部循环的起始位置不同,也稍微提高了一点点程序的执行效率。
#include<stdio.h>
int pri[100] = {0};
int main()
{
int n,cnt=0;
double t1 = clock();
scanf("%d", &n);
for (int i = 2; i <= n/i; i++)
{
if (!pri[i])//没被筛过
{
for (int j = i * i; j <= n; j += i)//去除合数
{
pri[j] = 1;
}
}
}
for (int i = 2; i <= n; i++)
{
if (!pri[i])
{
cnt++;
printf("%d\n", i);
}
}
return 0;
}
六、欧拉筛法
欧拉筛法是一种高效的求解素数的算法,其本质基于线性筛法的思想。
欧拉筛法的核心思想是筛去每个数的所有质因数,使得每个数只被它的最小质因数筛选一次。首先将2到n范围内的所有数都标记为素数,然后从2开始循环到n,对于每个数i,如果它是素数,则将其加入素数数组中,并将它的所有倍数(除了它本身)标记为合数。对于一个合数i*j,它已经被i筛选过了,所以其最小质因数一定是i,因此只需要将其标记一次即可。
另外,为了保证每个合数都只会被它的最小质因数筛选一次,算法中添加了一个优化条件,即当i能够整除当前质数数组中的某个数j时,直接将i*j标记为合数,跳过后续的循环过程。
欧拉筛法的时间复杂度为O(n),相比于朴素筛法和埃式筛法,具有更高的效率和更低的时间复杂度。
#include <stdio.h>
#include <stdbool.h>
int main()
{
int n = 100;
bool isPrime[101];
int prime[101];
int cnt = 0; //记录素数的个数
for (int i = 2; i <= n; i++)
isPrime[i] = true;
for (int i = 2; i <= n; i++)
{
if (isPrime[i])
{
prime[cnt++] = i; //将i加入素数数组
}
for (int j = 0; j < cnt && i * prime[j] <= n; j++)
{
isPrime[i * prime[j]] = false; //将当前数与质因数的积标记为合数
if (i % prime[j] == 0)
break; //优化,保证每个合数只会被它的最小质因数筛选一次
}
}
for (int i = 0; i < cnt; i++)
{
printf("%d\n", prime[i]);
}
return 0;
}
更多推荐
所有评论(0)