问题:

在该文的最后,曾提到完成的代码还有进一步改进的余地。本文完成了这个改进。所以本文讨论的并不是初学者代码中的常见错误与瑕疵,而是对我自己代码的改进和优化。标题只是为了保持系列的连续性。

改进

程序的总体思想没有改变,所以main()函数不需要任何改动。

int main( void)

{

unsigned n ;

puts("数据组数=?");

scanf("%u" , &n );while ( n -- > 0)

{intx ;

puts("整数X=?");

scanf("%d", &x );

printf("%d\n" , get_nearest( x ) ); //求最接近x的素数

}return 0;

}

进一步的改进体现在

typedefstructprime_list

{

unsigned prime;struct prime_list *next;

}

Node;int get_nearest( intx )

{int step = 0 ; //步长增量

int sign = -1; //符号

Node * head = NULL ; //素数链表

while ( ! be_prime( x , &head ) )

x+= ( sign = - sign ) * ++step ;

my_free(head) ;returnx ;

}

这里增加了一个链表head,用于存储素数表。这样,在判断素数时只用较小的素数试除就可以了,这可以使计算数量大为减少。因为与自然数相比,素数的数量很少(≈ln n / n 个)。此外,在判断完x是否为素数之后,如果需要判断下一个数(x += ( sign = - sign ) * ++ step ;)是否为素数,这个素数表还可以重复使用,最多再向其中添加一个素数就可以了。(注意最初素数表是空的)

判断素数的方法很简单,小学生都懂。

bool be_prime( int x , Node * * pp ) //根据素数表pp判断x是否为素数

{if ( x <= 1)return false;if ( x == 2)return true;if ( get_remainder( x , pp ) == 0 ) //x对素数表pp中素数逐个求余有0值

return false;return true;

}

但是由于素数表(*pp==NULL)可能是空的,因此

int get_remainder( int x , Node * * pp )//x对素数表pp中素数逐个求余

{while ( * pp == NULL || sqr_less( (*pp) -> prime , x ) )//表中素数个数不足

add_1_prime ( pp ) ; //表中增加一个素数

Node* p = *pp ;while ( p !=NULL )

{if ( x % p -> prime == 0)return 0;

p= p ->next ;

}return !0;

}bool sqr_less ( int n , intx )

{return n * n

}

需要先向其中添加素数

add_1_prime ( pp ) ;

直到

sqr_less( (*pp) -> prime , x )

依从小到大次序最后加入的那个素数的平方不小于x为止。

对于

void add_1_prime( Node * *pp )

{if ( * pp ==NULL )

{

add (2 , pp ); //第一个素数

return;

}int next_p = ( * pp )->prime + 1 ; //从最后一个素数之后开始找下一个素数

while ( !be_prime( next_p , pp ) )

next_p++;

add( next_p , pp );//将下一个素数加入素数表

}

来说,加入第一个素数——2很容易,但是寻找素数表中最大素数后的下一个素数时,却需要判断一个整数是否是素数

be_prime( next_p , pp )

这样,就会发现,这个过程最初是由判断某个数x是否是素数开始,

be_prime( x , & head )

在判断过程中需要建立素数表,

add_1_prime ( pp ) ;

而建立素数表,又需要判断某个数是否是素数

be_prime( next_p , pp )

这样就形成了一个极其复杂的间接递归调用。更为复杂的是,在调用的过程中,素数表本身即不断地被使用,而自身也处于不断的变化状态之中,即不断地被添加进新的素数,与复杂的间接递归一道,构成了比复杂更复杂的复杂的代码结构与复杂的数据结构的复杂的结合体。有兴趣的话可以自己算一下圈复杂度,如此复杂的情况通常并不容易遇到。

这种局面完全是由于精打细算造成的,由于对速度的斤斤计较,从而形成了一幅小猫在拼命咬自己尾巴同时小猫自己又在不断变化的复杂无比的动态画面。由此我们不难理解,为什么有人说,“不成熟的优化是万恶之源”(Premature optimization is the root of all evil!- Donald Knuth)。因为优化往往意味着引人复杂。复杂也是一种成本,而且是一种很昂贵的成本。

就这个题目而言这种成本应该算是值得,因为对于求一个较大的最接近的素数问题而言(例如对于109这个量级),两套代码的速度有天壤之别。

增强可读性?

如果把建立素数表的要求写在get_nearest()函数中,可能会使代码可读性变得更好些。

int get_nearest( intx )

{int step = 0 ; //步长增量

int sign = -1; //符号

Node * head = NULL ; //素数链表

while ( 建立最大素数平方不小于x的素数表() , ! be_prime( x , &head ) )

x+= ( sign = - sign ) * ++step ;

my_free(head) ;returnx ;

}

但这里的这个这个“,”是免不掉的,且圈复杂度不变。

至于这种写法是否真的改善了可读性,恐怕是见仁见智。

进一步提高效率

没什么更好的办法,只能用点“赖皮”手段,即充分运用已有的素数知识,帮计算机算出一部分素数。

void add_1_prime( Node * *pp )

{if ( * pp ==NULL )

{

add (2 , pp ); //第一个素数

return;

}switch ( ( * pp ) ->prime )

{case 2: add ( 3, pp );return;case 3: add ( 5, pp );return;/*这里可以依样写多个case,只要是按照素数从小到大的次序*/

default:

{int next_p = ( * pp )->prime + 1 ; //从最后一个素数之后开始找下一个素数

while ( !be_prime( next_p , pp ) )

next_p++;

add( next_p , pp );//将下一个素数加入素数表

return;

}

}

}

这里switch语句的结构非常有趣。

重构

/*

问题:

素数

在世博园某信息通信馆中,游客可利用手机等终端参与互动小游戏,与虚拟人物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语言初学者代码中的常见错误与瑕疵”系列博文

版本:V 2.1

*/

#include #includetypedefstructprime_list

{

unsigned prime;struct prime_list *next;

}

Node;int get_nearest( int);bool be_prime( int , Node * *);int get_remainder( int , Node * *) ;void add_1_prime( Node * *);bool sqr_less ( int , int);void add ( int , Node * *);void my_malloc( Node * *);void my_free( Node *);int main( void)

{

unsigned n ;

puts("数据组数=?");

scanf("%u" , &n );while ( n -- > 0)

{intx ;

puts("整数X=?");

scanf("%d", &x );

printf("%d\n" , get_nearest( x ) ); //求最接近x的素数

}return 0;

}int get_nearest( intx )

{int step = 0 ; //步长增量

int sign = -1; //符号

Node * head = NULL ; //素数链表

while ( ! be_prime( x , &head ) )

x+= ( sign = - sign ) * ++step ;

my_free(head) ;returnx ;

}bool be_prime( int x , Node * * pp ) //根据素数表pp判断x是否为素数

{if ( x <= 1)return false;if ( x == 2)return true;if ( get_remainder( x , pp ) == 0 ) //x对素数表pp中素数逐个求余有0值

return false;return true;

}int get_remainder( int x , Node * * pp )//x对素数表pp中素数逐个求余

{while ( * pp == NULL || sqr_less( (*pp) -> prime , x ) )//表中素数个数不足

add_1_prime ( pp ) ; //表中增加一个素数

Node* p = *pp ;while ( p !=NULL )

{if ( x % p -> prime == 0)return 0;

p= p ->next ;

}return !0;

}bool sqr_less ( int n , intx )

{return n * n

}//“偷奸耍滑”的add_1_prime()

void add_1_prime( Node * *pp )

{if ( * pp ==NULL )

{

add (2 , pp ); //第一个素数

return;

}switch ( ( * pp ) ->prime )

{case 2: add ( 3, pp );return;case 3: add ( 5, pp );return;/*这里可以依样写多个case,只要是按照素数从小到大的次序*/

default:

{int next_p = ( * pp )->prime + 1 ; //从最后一个素数之后开始找下一个素数

while ( !be_prime( next_p , pp ) )

next_p++;

add( next_p , pp );//将下一个素数加入素数表

return;

}

}

}//老老实实的add_1_prime()//void add_1_prime( Node * * pp )//{//if ( * pp == NULL )//{//add ( 2 , pp );//第一个素数//return ;//}//

//int next_p = ( * pp )->prime + 1 ;//从最后一个素数之后开始找下一个素数//

//while ( !be_prime( next_p , pp ) )//next_p ++ ;//

//add( next_p , pp );//将下一个素数加入素数表//}

void add ( int prime , Node * *pp )

{

Node*temp ;

my_malloc(&temp );

temp-> prime =prime ;

temp-> next = *pp ;* pp =temp ;

}void my_malloc( Node * *p_p )

{if ( ( * p_p = malloc( sizeof (* * p_p) ) ) ==NULL )

exit(1);

}void my_free( Node *p )

{

Node*temp ;while ( ( temp = p ) !=NULL )

{

p= p->next;

free( temp );

}

}

相关博客:

偶然发现Jingle Guo网友后来研究同一问题的一篇博文,我感觉对阅读此文的网友可能有一定的参考价值,故在此给出相关链接:

c语言间接级别不同_一个超复杂的间接递归——C语言初学者代码中的常见错误与瑕疵(6)...相关推荐

  1. 为什么c语言加法错误,分数的加减法——C语言初学者代码中的常见错误与瑕疵(12)...

    重构 题目的修正 我抛弃了原题中"其中a, b, c, d是一个0-9的整数"这样的前提条件,因为这种限制毫无必要.只假设a, b, c, d是十进制整数形式的字符序列. 我也不清 ...

  2. C语言初学者代码中的常见错误与瑕疵(9)

    题目 字母的个数 现在给你一个由小写字母组成字符串,要你找出字符串中出现次数最多的字母,如果出现次数最多字母有多个那么输出最小的那个. 输入:第一行输入一个正整数T(0<T<25) 随后T ...

  3. c语言一个数中是否含有8,要心中有“数”——C语言初学者代码中的常见错误与瑕疵(8)...

    在 飞鸟_Asuka网友指出"是不是时间复杂度比较大",并说他"第一眼看到我就想把它当成一个数学问题来做"之后,我又重新对问题进行了数学式的思考后发现的. 这个 ...

  4. C语言初学者代码中的常见错误与瑕疵(2)

    问题: 另一种阶乘 大家都知道阶乘这个概念,举个简单的例子:5!=1*2*3*4*5. 现在我们引入一种新的阶乘概念,将原来的每个数相乘变为i不大于n的所有奇数相乘 例如:5!!=1*3*5.现在明白 ...

  5. 中虚数怎么表示_英文论文写作中的常见错误

    之前写过一篇如何写中文论文,这次就写个英文论文写作中的常见错误吧.都是平时自己整理总结的,也是一路摸爬滚打的见证吧.如有错误,欢迎批评指正.未完待续...... 1.逗号粘连: 两个独立的句子间要用句 ...

  6. 编写代码、打印图4-2所示的图形python_Python之turtle库画各种有趣的图及源码(更新中)_一个超会写Bug的程序猿的博客-CSDN博客...

    原文作者:一个超会写Bug的安太狼 原文标题:Python之turtle库画各种有趣的图及源码(更新中) 发布时间:2021-02-09 03:35:11 Turtle库是Python语言中一个很流行 ...

  7. c语言解析sql语句_解析SQL语句比解析类C语言更麻烦?

    最近想做一个SQL语句解析器,换句话说想给自己的系统加上类似SQL语句的查询引擎.我之前做过一个解析类似C语言语法的解析器,可以解析 C/C++里的运算表达式,if-else-等基本语句.我以为做个S ...

  8. c语言编写u盘杀毒软件,一个简单的C++编写的u盘病毒代码

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 一个简单的C++编写的u盘病毒代码 一个win32下能用的U盘病毒 研究原理可以 别编译拿去害人就行 (ring3的病毒貌似也害不了人) 前久用IDA逆向 ...

  9. c语言贪吃蛇添加排行榜,c语言贪吃蛇排行榜_...12年4月编程语言排行榜 C语言荣归宝座...

    12年4月编程语言排行榜 C语言荣归宝座 JPG,902x531,131KB,424_250 C语言在目前的编程语言排行榜上占据头名的位置-全屏显示课程章节 JPG,500x267,232KB,467 ...

最新文章

  1. 用js判断时间的先后顺序
  2. Linux下Redis-3.0.7版本的安装以及Redis主备的部署(二)
  3. LeetCode 1016. 子串能表示从 1 到 N 数字的二进制串(bitset)
  4. GIT上传下载报错:[You do not have permission to pull from the repository]的解决方案!
  5. BeautifulSoup([your markup]) to this: BeautifulSoup([your markup], lxml) 解决未设置默认解析器的错误...
  6. catia怎么创建约束快捷键_CATIA快捷键整理版.doc
  7. 21天学习挑战赛——Python 操作Excel(xlrd和xlwt)
  8. 从杭州崩溃小伙说起:我们被灌输的价值观,真的对吗?
  9. 区分苹果开发者的网址(开发者网址和管理您的appid网址)及证书信息
  10. 7-15(查找) 航空公司VIP客户查询(25 分)
  11. 爱情就像是免杀,连鞋都没脱,就悄无声息的走进了你的心里
  12. navicat执行sql文件报错:1840-@@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.
  13. 在Firefox中以电影院风格观看YouTube视频
  14. 转:招聘就是战略,洞察面试三板斧
  15. 移动 电信 联通 APN cmwap cmnet ctwap ctnet 3gwap uniwap 3gnet uninet是...
  16. 微信浏览器禁止下载APK文件 微信扫描二维码 下载app的方法
  17. 数组,向量和矩阵以及空间的维数
  18. oracle 根据已有表创建新表
  19. coreldraw sp2精简版 x4_coreldraw下载
  20. 侍魂胧月传说服务器维护,12月9日停机维护更新公告

热门文章

  1. 【数学建模类比赛经验分享】——国赛
  2. Win10 Win11 安装和使用手记
  3. 微信开放平台开发第三方授权登陆:微信扫码登录
  4. 笔记本连接手机热点并共享网络给台式机
  5. CGB2108day17
  6. 能安装Chrome扩展和油猴脚本的手机浏览器
  7. Android中的PID,UID,TID
  8. error: failed to push some refs to如何解决
  9. AMD推两款集成CPU和GPU的新FirePro处理器
  10. canvas-随机粒子特效