c语言:实现对于给定的正整数N,依次打印出小于等于N的所有素数。两种方法及其优化

请编写一个程序,实现对于给定的正整数N,依次打印出小于等于N的所有素数。

方法一:试除法

由素数的定义得到如下程序:

#include<stdio.h>

int print_prime(int num)//prime表示素数

{

int i = 0;

for (i = 2; i <= num; i++)

{

int j = 0;

for (j = 2; j < i; j++)

{

if (i%j == 0)

{

break;

}

}

if (i==j)

{

printf("%d\t",i);

}

}

}

int main()

{

int num;

printf("请输入一个正整数:");

scanf("%d", &num);

if (num > 1)

{

printf("打印出所有的素数:");

print_prime(num);

}

else

{

printf("不存在小于等于%d的素数", num);

}

printf("\n");

return 0;

}

上面的代码中,在判断素数时一直从2试除到n-1。这里从n/2之后的数到n-1的试除显然是多余的,比如正整数7不能被2整除,必然不能被4,6整除。

优化1:

试除的范围优化到[2,n/2],则工作量减少一半,代码如下:

int print_prime(int num)//prime表示素数

{

int i = 0;

for (i = 2; i <= num; i++)

{

int j = 0;

for (j = 2; j <= i/2; j++)//修改部分

{

if (i%j == 0)

{

break;

}

}

if (j == (i/2+1))//修改部分

{

printf("%d\t", i);

}

}

}

既然能将试除范围优化到[2,n/2],那么这个范围是不是还能继续优化呢?答案是肯定的,在[2,n/2]这个范围里(√n,n/2]的试除也是多余的。因为因数是成对出现的,如16可分解为:1和16 、2和8、4和4、8和2、16和1。这些因数里必然有一个小于等于4。故只需试除小于等于√n的数就可以了。

优化2:

试除范围优化为[2,√n],代码如下:

#include<stdio.h>

#include<math.h>//修改部分

int print_prime(int num)//prime表示素数

{

int i = 0;

for (i = 2; i <= num; i++)

{

int j = 0;

for (j = 2; j <=sqrt(i); j++)//修改部分

{

if (i%j == 0)

{

break;

}

}

if (j > sqrt(i))//修改部分

{

printf("%d\t", i);

}

}

}

上面所有的代码在找素数的时候是从2到n,在这个范围内除了2之外的偶数都不是素数,所以可以跳过这些偶数。

还有试除范围内除了2之外的偶数也是没有必要的,因为如果不能被2整除,必然不能被大于2的偶数整除。

优化3:

寻找素数时和试除范围内跳过除2之外的偶数。代码如下:

int print_prime(int num)//prime表示素数

{

int i = 0;

printf("%d\t", 2);//修改部分

for (i = 3; i <= num; i+=2)//修改部分

{

int j = 2;

for (j = 3; j <= sqrt(i); j+=2)//修改部分

{

if (i%j == 0)

{

break;

}

}

if (j > sqrt(i))

{

printf("%d\t", i);

}

}

}

在上面的代码中,试除范围内的一些数也是不必要的。比如判断101是否为素数时,要分别试除小于10的2和所有奇数,即2、3、5、7、9,其中对9的试除是不必要的。即对所有的非素数的试除是不必要的,因为非素数必然可分解为比它小的素数的乘积,既然它的质因数不能整除某个数,这个数必然也不能。故试除的范围可缩小到小于等于√n的所有素数。

优化4:

只试小于√n的素数,那么问题来了,要试除这些素数时必然要将前面求出的素数保存起来,开辟多大一块空间合适呢?因为n的大小未知,所以无法确定开辟多少空间。这里暂时没有完全解决的办法,我的做法是先开辟大小为1000的空间,需要时再修改。

#include<stdio.h>

#include<math.h>

#define MAX 1000  //定义数组的大小

int print_prime(int num)//prime表示素数

{

int arr[MAX] = { 0 };

int i = 0,j=1;

printf("%d\t", 2);

arr[0] = 2;

for (i = 3; i <= num; i += 2)

{

int k = 0;

while (arr[k]>0&&arr[k]<=sqrt(i))

{

if (i%arr[k] == 0)

{

break;

}

k++;

}

if (!arr[k]||(arr[k] > sqrt(i)))

{

printf("%d\t", i);

arr[j] = i;

j++;

}

}

}

int main()

{

int num;

printf("请输入一个正整数:");

scanf("%d",&num);

if (num > 1)

{

printf("打印出所有的素数:");

print_prime(num);

}

else

{

printf("不存在小于等于%d的素数",num);

}

printf("\n");

return 0;

}

方法二:筛选法

这种方法求素数的思想就是,不断筛去最小的数的倍数。这个最小的数必然是素数。

比如最小的素数是2,去掉所有2的倍数;接下来最小的数是3,3就是素数,去掉所有的3的倍数;依次类推,直到最小的数小于等于√n为止。为什么是√n呢? 在上面的试除法中讲到只要试除小于等于√n的所有素数即可判断出小于等于n的所有素数,这里同样适用,只要去掉所有的小于等于√n的所有数的倍数,剩下的数就是小于等于n的所有素数。

代码如下:

#include<stdio.h>

#include<math.h>

#define MAX 1000  //定义数组的大小

int print_prime(int num)//prime表示素数

{

int arr[MAX] ;

int i = 0,j=0;

for (i = 0; i < num - 2; i++)//初始化数组[2,num]

{

arr[i] = i + 2;

}

while (arr[j]<=sqrt(num))//除数的范围

{

for (i = j + 1; i < num - 1; i++)

{

if (arr[i]%arr[j] ==0)//筛去arr[i]的倍数

{

arr[i] = 0;

}

}

j++;

while(!arr[j])//确定最小数

{

j++;

}

}

for (i = 0; i < num - 1; i++)

{

if (arr[i])

{

printf("%d\t",arr[i]);

}

}

}

int main()

{

int num;

printf("请输入一个正整数:");

scanf("%d",&num);

if (num > 1)

{

printf("打印出所有的素数:");

print_prime(num);

}

else

{

printf("不存在小于等于%d的素数",num);

}

printf("\n");

return 0;

}

上面的代码有一个很明显的缺陷就是开辟空间过大,如何来解决这个问题呢?

上述代码所开辟的空间为int型,占用4个字节,占用空间太多,可以构造一个bool型数组,bool型是一个基本的数据类型,占1个字节,以下标来存储数据,节省了75%的空间。

优化:

构造bool型数组,以下标来存储数据,每个数只占一个字节。

代码如下:

#include<stdio.h>

#include<stdbool.h>

#include<math.h>

#define MAX 1000  //定义数组的大小

void print_prime(int num)//prime表示素数

{

bool arr[MAX] ;

int i = 0,j=2;

for (i = 0; i <num ; i++)//初始化数组为真

{

arr[i] = 1;

}

while (arr[j]<=sqrt(num))//除数的范围

{

for (i = j + 1; i <= num ; i++)

{

if (i%j ==0)//筛去arr[i]的倍数

{

arr[i] = 0;

}

}

j++;

while(!arr[j])//确定最小数

{

j++;

}

}

for (i = 2; i <=num ; i++)//打印素数

{

if (arr[i])

{

printf("%d\t",i);

}

}

}

int main()

{

int num;

printf("请输入一个正整数:");

scanf("%d",&num);

if (num > 1)

{

printf("打印出所有的素数:");

print_prime(num);

}

else

{

printf("不存在小于等于%d的素数",num);

}

printf("\n");

return 0;

}

结果1:

请输入一个正整数:9

打印出所有的素数:2     3       5       7

请按任意键继续. . .

结果2:

请输入一个正整数:1

不存在小于等于1的素数

请按任意键继续. . .

时间: 2024-08-05 23:39:12

c语言:实现对于给定的正整数N,依次打印出小于等于N的所有素数。两种方法及其优化的相关文章

shell 脚本实现乘法口诀表的两种方法——shell与C语言

shell 脚本实现乘法口诀表的两种方法--shell与C语言 话不多说直接给出代码: 1 #!/bin/bash 2 if [ $# -eq 0 ] //用于判断输入的参数个数为0 3 then 4 echo "welcome you!" 5 echo "this is a test with 2 methods to output arbitrarily mux table!" 6 else 7 echo "sorry you input invlia

递归很耗内存+多项式求值的两种方法+c语言计时方法

1.用for循环写一个函数,实现从1开始输出到N的正整数. 有两宗实现方法,一种是递归,另一种是非递归 //非递归 void PrintN1(int N){ int i; for(i=1;i<=N;i++){ printf("%d\n",i); } return; } //递归 递归对空间的需求很大,当数字很大的时候,需要很大的内存,当数字是十万的时候递归就崩了 void PrintN2(int N){ if(N){ PrintN2(N-1); printf("%d\n

C语言播放声音最简单的两种方法

1. 假设仅须要播放波形文件wav格式的声音,非常easy.仅仅需一句话: PlaySound(TEXT("Data\\1.wav"), NULL, SND_FILENAME | SND_ASYNC | SND_LOOP); 在这里仅仅提供方法,具体问题自己去探索. 完整C语言代码: #include <windows.h> #pragma comment(lib, "Winmm.lib") int main(int argc, char *argv[]

在linux下Lua调用C语言的两种方法

一.c语言作为应用程序的一部分 #include <stdio.h>#include <string.h>#include <lua.hpp>#include <lauxlib.h>#include <lualib.h> //待Lua调用的C注册函数.static int add2(lua_State* L){ //检查栈中的参数是否合法,1表示Lua调用时的第一个参数(从左到右),依此类推. //如果Lua代码在调用时传递的参数不为number

两种方法判断一个字符串是否为另外一个字符串旋转之后的字符串。(C语言)

例如:给定s1 = AABCD和s2 = BCDAA,返回1给定s1 = abcd和s2 = ACBD,返回0. AABCD左旋一个字符得到ABCDAAABCD左旋两个字符得到BCDAA 第一种:通过其中一个字符串向左移动确定第二个字符串 #include<stdio.h> #include<string.h> void left_move(char *str, int k) { while (k != 0) { char *cur = str;//必须需要另一个指针存储首地址 c

c语言:两种方法实现字符串拷贝strcpy

实现字符串拷贝strcpy 方法一: // 字符串拷贝函数的实现 #include<stdio.h> #include<assert.h> void my_strcpy(char *dest,  char *src)//src表示source源,dest目标 { assert(dest!=NULL); assert(src); char *ret = dest; while (*dest++ = *src++) { ; } return dest; } int main() { c

c语言:把只含因子2、3和5的数称为丑数,求按从小到大的顺序的第1500个丑数(两种方法比较)

把只含因子2.3和5的数称为丑数,求按从小到大的顺序的第1500个丑数.例如6.8都是丑数,但14不是,因为它包含因子7.习惯上把1当作第1个丑数. 算法1:逐个判断每个整数是不是丑数的解法,直观但不够高效 #include<stdio.h> int ugly(int number) { while (number % 2 == 0) { number /= 2; } while (number % 3 == 0) { number /= 3; } while (number % 5 == 0

Java编程:用两种方法求输入正整数的位数。

import java.util.Scanner; public class Test { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); //把整数n转换为字符串求其长度 int len = Integer.toString(n).length(); System.out.println("用字符串的方式求其长度len="+len);

shell中调用R语言并传入参数的两种方法

第一种: Rscript myscript.R R脚本的输出 第二种: R CMD BATCH myscript.R # Check the output cat myscript.Rout 调用R脚本的全部控制台log 传入参数: 在脚本中add args<-commandArgs(TRUE) 然后shell中: Rscript myscript.R arg1 arg2 arg3 注意取出来的参数是所有参数连在一起的character