素数html
在世博园某信息通讯馆中,游客可利用手机等终端参与互动小游戏,与虚拟人物Kr. Kong 进行猜数比赛。数据结构
当屏幕出现一个整数X时,若你能比Kr. Kong更快的发出最接近它的素数答案,你将会得到一个意想不到的礼物。函数
例如:当屏幕出现22时,你的回答应是23;当屏幕出现8时,你的回答应是7;post
若X自己是素数,则回答X;若最接近X的素数有两个时,则回答大于它的素数。测试
输入:第一行:N 要竞猜的整数个数url
接下来有N行,每行有一个正整数Xspa
输出:输出有N行,每行是对应X的最接近它的素数设计
样例:输入rest
4code
22
5
18
8
输出
23
5
19
7
1 #include <stdio.h>
2 int prime(int x) //判断是否为素数
3 { 4 int i; 5
6 if (x==1) 7 return 0; 8
9 for(i=2;i<x;i++) 10 { 11 if(!(x%i)) 12 return 0; 13 } 14
15 return 1; 16 } 17
18 int main() 19 { 20 int i,j,n,a[6],temp1,temp2; 21 int prime(int x); 22
23 //printf("n=");
24 scanf("%d",&n); 25
26 for (i=0;i<n;i++) //输入相应数值
27 { 28 scanf("%d",&a[i]); 29 } 30
31 for(i=0;i<n;i++) 32 { 33
34 temp1=(temp2=a[i]); 35 for(j=a[i];;j++) //求出大于等于数值的素数
36 { 37 if(prime(j)) 38 { 39 temp1=j; 40 break; 41 } 42 } 43
44 for(j=a[i]-1;;j--) //求出小于数值的素数
45 { 46 if(prime(j)) 47 { 48 temp2=j; 49 break; 50 } 51 } 52
53 printf("%d\n",temp1-a[i]<=a[i]-temp2?temp1:temp2); 54 } 55
56 return 0; 57 }
int i,j,n,a[6],temp1,temp2; int prime(int x);
老问题,变量太多,数据结构设计不合理,函数类型定义位置不当。实际上这种问题有一个以不变应万变的“标准”句型:
#include <stdio.h> int main( void ) { int n ; scanf("%d" , &n); while ( n -- > 0 ) { //定义解决问题须要的变量 //输入测试数据 //解决问题 } return 0; }
其中,裸体的“scanf("%d" , &n);”只在这种特定状况(刷题)下能够接受,对于通常状况,在输入前给用户一个提示信息为好。
for (i=0;i<n;i++) //输入相应数值 { scanf("%d",&a[i]); } for(i=0;i<n;i++) { temp1=(temp2=a[i]); for(j=a[i];;j++) //求出大于等于数值的素数 { if(prime(j)) { temp1=j; break; } } for(j=a[i]-1;;j--) //求出小于数值的素数 { if(prime(j)) { temp2=j; break; } } printf("%d\n",temp1-a[i]<=a[i]-temp2?temp1:temp2); }
显然应该合并为一个for语句。原做者大概误觉得必须所有输入以后才能开始解决问题,估计是题目对输入输出样式的说明容易让人误解,实际上输入与输出是相互独立的。没必要拘泥于先所有输入后再开始逐个解决问题。
for(j=a[i];;j++) //求出大于等于数值的素数 { if(prime(j)) { temp1=j; break; } } for(j=a[i]-1;;j--) //求出小于数值的素数 { if(prime(j)) { temp2=j; break; } }
这里存在几方面的问题,下面逐个评述。
首先,抛开具体构思,仅仅从代码形式上来讲这中写法就是不能容忍的。
代码写得必定要“拽”(DRY)(参见代码写得要"拽"(DRY)——《C解毒》试读)。
而这里的两条for语句,本质上是如出一辙的。能够说,一个是对另外一个的复制粘贴(若是写的时候连复制粘贴都没用到,而是老老实实一个字一个写成的,那就更成问题了。那说明连计算机都不会用)。
因此不管如何这两条语句都应该想到用函数实现:
temp1 = find_prime(a[i],1); //求从a[i]开始的第一个素数(步长为1); temp2 = find_prime(a[i]-1,-1);//求从a[i]-1开始的第一个素数(步长为-1);
这样写代码要漂亮多了。
再说一下逻辑上的缺陷。
“大于等于”的素数求出以后,若是是“等于”,那么问题已经得解,就不必执行第二条循环语句了,因此应该跳过第二条循环语句。
for( i = 0 ; i < n ; printf("%d\n",solve) , i++ ) { for(j=a[i];;j++) //求出大于等于数值的素数 { //…… } if ( temp1 == a[i] ) { //输出该素数(temp1或a[i]) continue ; //转到计算下一个a[i] } for(j=a[i]-1;;j--) //求出小于数值的素数 { //…… } printf("%d\n",temp1-a[i]<=a[i]-temp2?temp1:temp2); }
若是用函数求下一个素数,代码还能够写得更漂亮一些:
for(i=0;i<n;i++) { int temp1 , temp2 ; if ( (temp1 = find_prime(a[i],1)) == a[i] ) { printf("%d\n",temp1);//输出temp1 continue ; } temp2 = find_prime(a[i]-1,-1) printf("%d\n",temp1-a[i]<=a[i]-temp2?temp1:temp2); }
这显然要比原来的代码干净整洁多了。若是以为两条printf()调用很差,还能够这样写(又是逗号表达式,呵呵):
for( i = 0 ; i < n ; printf("%d\n",solve) , i++ ) { int temp1 , temp2 , solve ; if ( (temp1 = find_prime(a[i],1)) == a[i] ) { solve = temp1 ; continue ; } temp2 = find_prime(a[i]-1,-1) solve = temp1-a[i]<=a[i]-temp2?temp1:temp2 ; }
此外要说一下的是,不管是在main()中仍是在prime()函数中,做者都没有认真考虑当输入整数并不处于两个素数之间的状况。事实上这是有可能的。好比输入为1,那么找小于1的素数是不可能的。但在原代码中,明显能看出做者根本就没意识到这件事(main()中没有相关的处理,prime()函数没有对0和负整数进行判断)。这应该说是原代码中的一个BUG。
最后再说一下整体思路方面的问题。
做者的思路是先“求出大于等于数值的素数”,再“求出小于数值的素数”。这个思路可行,但不够好。比较好的思考应该是:
这种想法更天然,一旦找到素数就完活儿,而不会作无用功。
而原做者的代码则存在这样的可能:先求出一个比X大不少的素数,但其实解是X-1;或者求出大于X的素数是X+1,又去找出一个比X小不少的素数。这两种状况下,计算机总要作一些很无聊且无心义的工做。
固然,原代码还能够继续改进,但在错误的整体思路指导下,改进的空间极其狭仄。即便改了,也没法完全根除指令雍余的问题。
而按照下面次序寻找最近素数
X X+1 X-1 X+2 X-2 X+3……
则不存在相似问题。
不难发现,这个序列相邻两项差的绝对值,刚好构成天然数列:1 2 3 4 5 ……
据此,重构以下:
/* 问题: 素数 在世博园某信息通讯馆中,游客可利用手机等终端参与互动小游戏,与虚拟人物Kr. Kong 进行猜数比赛。 当屏幕出现一个整数X时,若你能比Kr. Kong更快的发出最接近它的素数答案,你将会得到一个意想不到的礼物。 例如:当屏幕出现22时,你的回答应是23;当屏幕出现8时,你的回答应是7; 若X自己是素数,则回答X;若最接近X的素数有两个时,则回答大于它的素数。 输入:第一行:N 要竞猜的整数个数 接下来有N行,每行有一个正整数X 输出:输出有N行,每行是对应X的最接近它的素数 样例:输入 4 22 5 18 8 输出 23 5 19 7 做者:薛非 出处:http://www.cnblogs.com/pmer/ “C语言初学者代码中的常见错误与瑕疵”系列博文 */ #include <stdio.h> #include <stdbool.h> int get_nearest( int); bool be_prime( int ); int main( void ) { unsigned n ; puts( "数据组数=?" ); scanf("%u" , &n); //这里没写输入提示
while ( n -- > 0u ) { int x ; scanf("%d" , & x); printf("%d\n" , get_nearest( x ) ); } return 0; } int get_nearest( int x ) { int n , s ;//步长增量,符号
for ( n = 0 , s = -1 ; ! be_prime( x ) ; n ++ , s = -s , x += s * n ) { } return x ; } bool be_prime( int x ) { int fac ; if ( x <= 1 ) return false ; for ( fac = 2 ; fac * fac <= x ; fac ++ ) if ( x % fac == 0 ) return false ; return true ; }
重构的代码中,be_prime()函数,仅仅从功能角度来讲并无什么问题。但若放在问题的背景下,它的效率过低了。这个函数须要反复地对每个x都进行for ( fac = 2u ; fac * fac <= x ; fac ++ )这样的循环判断,若是问题要求判断的只有一个整数,这种写法也许无可厚非。但问题要求判断的是X X+1 X-1 X+2 X-2 X+3……这样一个序列,这样的写法就很是笨拙了。可是若想进一步改进却也不那么容易。此次就不改了,我将在后续的博文中给出改进的写法。