C语言循环的小艺术(1) by Misakamm

  • 格式:pdf
  • 大小:142.56 KB
  • 文档页数:10

1质数判断1

C语言循环的小艺术(1)

作者:Misakamm

博客:http://misakamm.com

前言

写代码,有两类追求,一种是追求实用(Coder),一种是追求代码艺术(Artist)我是

那种追实用追腻了,偶然追一下艺术(就是偶然和艺术有一腿)的那种Coder。

本文作者Misakamm见网上故意删除作者信息然后发布的行为,觉得很不愉快,特此重

新精细排版此文,并精简和美化代码,发布连载文。本文采用Latex排版。

很多人,已经习惯了for(i=0;i

高,但在特殊场合,特殊的循环写法,不但能提升循环的效率,还能使代码更精巧

1质数判断

对于这个,很多人可能会直接这样写

1intisPrime(intn)//函数返回1表示是质数,返回0表示不是质数

2{

3inti;

4for(i=2;i

5if(n%i==0)

6break;

7returni>=n;

8}

又或者,有的人知道平方根的优化:

1intisPrime(intn)

2{

3inti,s=(int)sqrt(n+0.01);

4for(i=2;i<=s;i++)

5if(n%i==0)

6break;

7returni>s;

8}

再或者,消除偶数:

1intisPrime(intn)

2{1质数判断2

3inti,s=(int)sqrt(n+0.01);

4if(n<=3)

5return1;

6if(n%2==0)

7return0;

8for(i=3;i<=s;i+=2)

9if(n%i==0)

10break;

11returni>s;

12}

当然,这样还不是很够的话,我们可以考虑这个事实:所有大于4的质数,被6除

的余数只能是1或者5。比如接下来的5,7,11,13,17,19都满足。所以,我们可以

特殊化先判断2和3。但后面的问题就出现了,因为并非简单的递增,从5开始是

+2,+4,+2,+4,....这样递增的。这样的话,循环应该怎么写呢?

首先,我们定义一个步长变量step,循环大概是这样

for(i=5;i<=s;i+=step)

那么,就是每次循环,让step从2变4,或者从4变2

于是,可以这么写:

1#include

2#include

3intisPrime(intn)

4{

5inti,s=(int)sqrt(n+0.01),step=4;

6if(n<=3)

7return1;

8if(n%2==0)

9return0;

10if(n%3==0)

11return0;

12for(i=5;i<=s;i+=step)

13{

14if(n%i==0)

15break;

16step^=6;

17}

18returni>s;

19}

20intmain(void)

21{1质数判断3

22intn;

23for(n=2;n<100;++n)//找出2-100的质数并输出

24{

25if(isPrime(n))

26printf(”%d,”,n);

27}

28getchar();

29return0;

30}

如上代码,一个stepˆ=6;完成step在2和4之间转换(这个ˆ符号是C

里的异或运算)

理由是,2化二进制是010,4是100,6是110,于是2异或4得到6:

2ˆ4=>6

6ˆ2=>4

6ˆ4=>2

于是利用异或,就可以构造这种步长在两个值之间来回变化的循环

思考题:前面说的是双值循环,那么如何构造三值或者四值循环?2菱形打印4

2菱形打印

很多人,打印菱形在控制台的思路是,把菱形上下拆分,分两段很接近的代码来打印

其实这样代码很不好看,并且不好阅读

我们知道,要打印的图案是这种:

1*

2***

3*****

4***

5*

满足上下对称,左右对称,那么,你能不能也弄一个二重循环,同样是对称的?很简单,

首先我们要抛开习惯性思维,for循环不一定要在0开始或者0结束。我们可以让循环从

-c到c,这样不就轻松产生一个对称的吗?(只要取个绝对值)我们把菱形的中心看成是坐

标0,0,那么,会输出星号的坐标,是|x|+|y|󰀁c的点

由此可得

1#include

2#include

3voidprint(intsize)//size是这个菱形的半径,直径会是size*2+1

4{

5intx,y;

6for(y=−size;y<=size;y++)

7{

8for(x=−size;x<=size;x++)

9{

10//x和y各自的绝对值的和,即|x|+|y|<=size

11if(abs(x)+abs(y)<=size)

12putchar(’*’);

13else

14putchar(’␣’);

15}

16putchar(’\n’);

17}

18}

19intmain(void)

20{

21print(5);//输出一个半径为的菱形5

22getchar();

23return0;

24}2菱形打印5

当然,有些人还会使用三目运算把代码简化成以下这个样子:

1voidprint(intsize)

2{

3intx,y;

4for(y=−size;y<=size;y++,putchar(’\n’))

5for(x=−size;x<=size;x++)

6putchar(abs(x)+abs(y)<=size?’*’:’␣’);7}

不过下文就不使用以上写法了。

如果需要得到空心菱形呢?非常非常简单,因为菱形边界上的点,满足的是|x|+|y|=c

所以,我们只要把那个if里的小于等于号,改成双等于号==就可以了

再类似地,如果我不要*号,我要最外层是字母A,然后里一层是B这样呢?即

1A

2ABA

3ABCBA

4ABA

5A

那么,我们只要在putchar那里做一个字符计算:

1voidprint(intsize)

2{

3intx,y;

4for(y=−size;y<=size;y++)

5{

6for(x=−size;x<=size;x++)

7{

8if(abs(x)+abs(y)<=size)

9putchar(’A’+(size−abs(x)−abs(y));//留意这里的计算方法10else

11putchar(’␣’);

12}

13putchar(’\n’);

14}

15}

类似地,如果我们要打印的是X形:2菱形打印6

1**

2**

3*

4**

5**

同样可以利用这个思路完成,这题就作为思考题吧3奇数阶幻方7

3奇数阶幻方

所谓幻方(最基本的那种),就是横,竖,对角线上的数的和等于一个常数的数字方阵

438

951

276

以上这个图,有什么规律?容易写成代码吗?

我们把这个图,向右复制五次,向下复制三次,展开一下:

438438438438438

95[1]951951951951

276[2]76276276276

4384[3]8[4]38438438

9519519[5]1951951

27627627[6]2[7]6276

43843843843[8]438

951951951951[9]51

276276276276276

注意括号数字的走向

怎么样,现在呢?

现在看起来显得规律性强了很多,但是,你会不会觉得循环还是不太好写?

我们如何从一个给定的n,直接得知它的坐标呢?

不难,找一下规律就可以发现对于任意的数值n+1有(以左上角为0,0坐标):

x=2+n+⌊n3⌋

y=1+n−⌊n3⌋

其实这个规律可以简单扩展到任意奇数阶幻方(以下size是奇数):

x=⌊size2⌋+1+n+⌊nsize⌋

y=⌊size2⌋+n−⌊nsize⌋

这样,我们就可以把原来复杂的循环,化简成一重简单循环

于是Misakamm有程序

1#include

2#defineSIZE5//定义幻方阶数,这个数只能是奇数

3intmain(void)

4{

5intx,y,i,sqSize,hSize;

6intsqMap[SIZE][SIZE];

7sqSize=SIZE*SIZE;3奇数阶幻方8

8hSize=SIZE/2;

9//计算1至SIZE*SIZE的数的位置并记录

10for(i=0;i

11{

12x=hSize+1+i+i/SIZE;

13y=hSize+i−i/SIZE;

14sqMap[y%SIZE][x%SIZE]=i+1;

15}

16//以下是输出

17for(y=0;y

18{

19for(x=0;x

20printf(”%4d”,sqMap[y][x]);

21puts(””);

22}

23return0;

24}

这个比你网上能找到的很多求奇数阶幻方的代码都短小很多(不过网上较多称之为魔方

阵,不知为何)