1. 写代码,有两类追求,一种是追求实用(Coder),一种是追求代码艺术(Artist
  2. 我是那种追实用追腻了,偶然追一下艺术(就是偶然和艺术有一腿)的那种Coder
  3.  
  4. 很多人,已经习惯了for(i=0; i<n; i++)这种单调的循环,虽然这的确的使用率最高,
  5. 但在特殊场合,特殊的循环写法,不但能提升循环的效率,还能使代码更精巧
  6.  
  7. 1. 质数判断
  8. 对于这个,很多人可能会直接这样写:
  9. int isPrime(int n) //函数返回1表示是质数,返回0表示不是质数
  10. {
  11. int i;
  12. for (i = 2; i < n; i++)
  13. if (n % i == 0)
  14. break;
  15. return i >= n;
  16. }
  17.  
  18. 又或者,有的人知道平方根的优化:
  19. int isPrime(int n)
  20. {
  21. int i, s = (int)(sqrt((double)n) + 0.01);
  22. for (i = 2; i <= s; i++)
  23. if (n % i == 0)
  24. break;
  25. return i > s;
  26. }
  1. 再或者,消除偶数:
  2. int isPrime(int n)
  3. {
  4. int i, s = (int)(sqrt((double)n) + 0.01);
  5. if (n <= 3) return 1;
  6. if (n % 2 == 0) return 0;
  7. for (i = 3; i <= s; i += 2)
  8. if (n % i == 0)
  9. break;
  10. return i > s;
  11. }
  12.  
  13. 当然,这样还不是很够的话,我们可以考虑这个事实:
  14. 所有大于4的质数,被6除的余数只能是1或者5
  15. 比如接下来的5,7,11,13,17,19都满足
  16.  
  17. 所以,我们可以特殊化先判断23
  18. 但后面的问题就出现了,因为并非简单的递增,从5开始是+2,+4,+2,+4,....这样递增的
  19. 这样的话,循环应该怎么写呢?
  20.  
  21. 首先,我们定义一个步长变量step,循环大概是这样 for (i = 5; i <= s; i += step)
  22. 那么,就是每次循环,让step24,或者从42
  23. 于是,可以这么写:
  24.  
  25. #include <stdio.h>
  26. #include <math.h>
  27.  
  28. int isPrime(int n)
  29. {
  30. int i, s = (int)(sqrt((double)n) + 0.01), step = 4;
  31. if (n <= 3) return 1;
  32. if (n % 2 == 0) return 0;
  33. if (n % 3 == 0) return 0;
  34. for (i = 5; i <= s; i += step)
  35. {
  36. if (n % i == 0)
  37. break;
  38. step ^= 6;
  39. }
  40. return i > s;
  41. }
  42.  
  43. int main()
  44. {
  45. int n;
  46. for (n = 2; n < 100; ++n) //找出 2 - 100 的质数并输出
  47. {
  48. if (isPrime(n)) printf("%d,", n);
  49. }
  50. getchar();
  51. return 0;
  52. }
  53.  
  54. 如上代码,一个 step ^= 6; 完成step24之间转换(这个 ^ 符号是C里的异或运算)
  55. 理由是,2化二进制是01041006110,于是2异或4得到6
  56. 2 ^ 4 => 6
  57. 6 ^ 2 => 4
  58. 6 ^ 4 => 2
  59.  
  60. 于是利用异或,就可以构造这种步长在两个值之间来回变化的循环
  61. 思考题:前面说的是双值循环,那么如何构造三值或者四值循环?
  62.  
  63. 2.菱形打印
  64.  
  65. 很多人,打印菱形在控制台的思路是,把菱形上下拆分,分两段很接近的代码来打印,
  66. 其实这样代码很不好看,并且不好阅读
  67. 我们知道,要打印的图案是这种:
  68. *
  69. ***
  70. *****
  71. ***
  72. *
  73.  
  74. 满足上下对称,左右对称,那么,你能不能也弄一个二重循环,同样是对称的?
  75. 很简单,首先我们要抛开习惯性思维,for循环不一定要在0开始或者0结束
  76. 我们可以让循环从 -c c ,这样不就轻松产生一个对称的吗?(只要取个绝对值)
  77. 我们把菱形的中心看成是坐标0,0,那么,会输出星号的坐标,是 |x| + |y| <= c 的点
  78.  
  79. 由此可得
  80. #include <stdio.h>
  81. #define IABS(x) ( (x) >= 0 ? (x) : -(x) ) //定义一个计算绝对值的宏
  82. void print(int size) // size是这个菱形的半径,直径会是size * 2 + 1
  83. {
  84. int x, y;
  85. for (y = -size; y <= size; y++)
  86. {
  87. for (x = -size; x <= size; x++)
  88. {
  89. if ( IABS(x) + IABS(y) <= size ) //x和y各自的绝对值的和,即 |x| + |y| <= size
  90. putchar('*');
  91. else
  92. putchar(' ');
  93. }
  94. putchar('\n');
  95. }
  96. }
  97.  
  98. int main()
  99. {
  100. print(5); //输出一个半径为5的菱形
  101. getchar();
  102. return 0;
  103. }
  104.  
  105. 如果我需要得到空心菱形呢?非常非常简单,因为菱形边界上的点,满足的是|x| + |y| == c
  106. 所以,我们只要把那个if里的小于等于号,改成双等于号 == 就可以了
  107.  
  108. 再类似地,如果我不要*号,我要最外层是字母A,然后里一层是B这样呢?即:
  109. A
  110. ABA
  111. ABCBA
  112. ABA
  113. A
  114.  
  115. 那么,我们只要在putchar那里做一个字符计算:
  116. void print(int size) // size是这个菱形的半径,直径会是size * 2 + 1
  117. {
  118. int x, y;
  119. for (y = -size; y <= size; y++)
  120. {
  121. for (x = -size; x <= size; x++)
  122. {
  123. if ( IABS(x) + IABS(y) <= size ) //x和y各自的绝对值的和,即 |x| + |y| <= size
  124. putchar( 'A' + (size - IABS(x) - IABS(y)) ); //留意这里的计算方法
  125. else
  126. putchar(' ');
  127. }
  128. putchar('\n');
  129. }
  130. }
  131.  
  132. 类似地,如果我们要打印的是X形:
  133. * *
  134. * *
  135. *
  136. * *
  137. * *
  138. 同样可以利用这个思路完成,这题就作为思考题吧
  139.  
  140. 3. 奇数阶幻方
  141. 所谓幻方(最基本的那种),就是横,竖,对角线上的数的和等于一个常数的数字方阵
  142. 4 3 8
  143. 9 5 1
  144. 2 7 6
  145.  
  146. 以上这个图,有什么规律?容易写成代码吗?
  147.  
  148. 我们把这个图,向右复制五次,向下复制三次,展开一下:
  149.  
  150. 4 3 8 4 3 8 4 3 8 4 3 8 4 3 8
  151. 9 5 [1] 9 5 1 9 5 1 9 5 1 9 5 1
  152. 2 7 6 [2] 7 6 2 7 6 2 7 6 2 7 6
  153. 4 3 8 4 [3] 8 [4] 3 8 4 3 8 4 3 8
  154. 9 5 1 9 5 1 9 [5] 1 9 5 1 9 5 1
  155. 2 7 6 2 7 6 2 7 [6] 2 [7] 6 2 7 6
  156. 4 3 8 4 3 8 4 3 8 4 3 [8] 4 3 8
  157. 9 5 1 9 5 1 9 5 1 9 5 1 [9] 5 1
  158. 2 7 6 2 7 6 2 7 6 2 7 6 2 7 6
  159.  
  160. 注意中括号数字的走向
  161. 怎么样,现在呢?
  162. 现在看起来显得规律性强了很多,但是,你会不会觉得循环还是不太好写?
  163. 我们如何从一个给定的n,直接得知它的坐标呢?
  164. 不难,找一下规律就可以发现对于任意的数值n+1有(以左上角为0,0坐标):
  165. x = 2 + n + n / 3;
  166. y = 1 + n - n / 3;
  167.  
  168. 其实这个规律可以简单扩展到任意奇数阶幻方(以下size是奇数):
  169. x = size / 2 + 1 + n + n / size; (注意这里的除法是取整除法,不带小数)
  170. y = size / 2 + n - n / size;
  171.  
  172. 这样,我们就可以把原来复杂的循环,化简成一重简单循环
  173.  
  174. 于是有程序:
  175. #include <stdio.h>
  176. #define SIZE 5 //定义幻方阶数,这个数只能是奇数
  177. int main()
  178. {
  179. int x, y, i, sqSize, hSize;
  180. int sqMap[SIZE][SIZE];
  181. sqSize = SIZE * SIZE;
  182. hSize = SIZE / 2;
  183. //计算1至SIZE * SIZE的数的位置并记录
  184. for ( i = 0; i < sqSize; i++)
  185. {
  186. x = hSize + 1 + i + i / SIZE;
  187. y = hSize + i - i / SIZE;
  188. sqMap[y % SIZE][x % SIZE] = i + 1;
  189. }
  190. //以下是输出
  191. for (y = 0; y < SIZE; y++)
  192. {
  193. for (x = 0; x < SIZE; x++)
  194. printf("%4d", sqMap[y][x]);
  195. puts("");
  196. }
  197. return 0;
  198. }
  199.  
  200. 这个比你网上能找到的很多求奇数阶幻方的代码都短小很多(不过网上较多称之为魔方阵,不知为何)
  201.  
  202. 4. 字符串循环移位
  203.  
  204. 问题,给你一个字符串,要求循环左移n
  205. 比如对"abcdefg" 循环左移2位,我们要得到"cdefgab"
  206.  
  207. 附加条件,不能使用连续辅助空间(包括动态分配),只能使用若干单个变量(即O(1)空间)
  208.  
  209. 首先,我们知道,反转一个字符串操作("abcd""dcba"),是不需要额外数组辅助的,只要头尾数据交换就可以了
  210. 然而,可能你不知道,仅仅使用字符串反转可以实现字符串循环移位:
  211.  
  212. //反转字符串,把st与ed所指向的中间的内容反转(包含st不包含ed)
  213. void str_rev(char* st, char *ed)
  214. {
  215. for (--ed; st < ed; ++st, --ed)
  216. {
  217. char c;
  218. c = *st; *st = *ed; *ed = c;
  219. }
  220. }
  221.  
  222. //用三反转等效左移字符串(st与ed之间,包含st不包含ed的内容)
  223. char* str_shl(char* st, char* ed, int n)
  224. {
  225. str_rev(st, &st[n]);
  226. str_rev( &st[n], ed);
  227. str_rev(st, ed);
  228. return st;
  229. }
  230.  
  231. #include <stdio.h>
  232. #include <string.h>
  233. int main()
  234. {
  235. char str[] = "abcdefghijklmnopqrstuvwxyz";
  236. puts( str_shl(str, str + strlen(str), 6) );
  237. getchar();
  238. return 0;
  239. }
  240.  
  241. 这里,如果要循环左移n位,只要把原来字符串分成两段,前n字符,和后面其它字符
  242. 两段分别反转,最后再整体反转,就实现了循环左移(如果先整体再两部分,就是循环右移)
  243.  
  244. 而在那个字符串反转函数里,参与循环的,不再是int,而是两个指针,
  245. 为什么选择使用两个指针呢?如果你写一个str_rev(char* str, int len)的版本,相信你就明白了,这里不多废话

C语言循环小技巧的更多相关文章

  1. 嵌入式C语言优化小技巧

    嵌入式C语言优化小技巧 1 概述 嵌入式系统是指完成一种或几种特定功能的计算机系统,具有自动化程度高,响应速度快等优点,目前已广泛应用于消费电子,工业控制等领域.嵌入式系统受其使用的硬件以及运行环境的 ...

  2. 【javascript】 for循环小技巧

    最近在读[Jquery技术内幕],里面介绍了一种js for循环的实用写法. 一般写for循环是这么写的: var elemts = [1,2,3,4,5]; for(var i=0; i<el ...

  3. *C语言的小技巧

    计算数组长度 ,,,,}; int Length=sizeof(a)/sizeof(int); 交换a和b的值,不借用辅助变量 a=a+b; b=a-b; a=a-b; 将0-9的字符转化为整数 '; ...

  4. C语言调试小技巧

    经常看到有人介绍一些IDE或者像gdb这样的调试器的很高级的调试功能,也听人说过有些牛人做工程的时候就用printf来调试,不用特殊的调试器.特别是在代码经过编译器一些比较复杂的优化后,会变得“难以辨 ...

  5. HDU 5895 Mathematician QSC(矩阵乘法+循环节降幂+除法取模小技巧+快速幂)

    传送门:HDU 5895 Mathematician QSC 这是一篇很好的题解,我想讲的他基本都讲了http://blog.csdn.net/queuelovestack/article/detai ...

  6. C语言中的调试小技巧

    C语言中的调试小技巧 经常看到有人介绍一些IDE或者像gdb这样的调试器的很高级的调试功能,也听人说过有些牛人做工程的时候就用printf来调试,不用特殊的调试器.特别是在代码经过编译器一些比较复杂的 ...

  7. Python语言防坑小技巧

    Python语言防坑小技巧 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.赋值即定义  1>.运行以下代码会出现报错 #!/usr/bin/env python #_*_ ...

  8. 一些Python的惯用法和小技巧:Pythonic

    Pythonic其实是个模糊的含义,没有确定的解释.网上也没有过多关于Pythonic的说明,我个人的理解是更加Python,更符合Python的行为习惯.本文主要是说明一些Python的惯用法和小技 ...

  9. ( 译、持续更新 ) JavaScript 上分小技巧(三)

    最近家里杂事较多,自学时间实在少的可怜,所以都在空闲时间看看老外写的内容,学习之外顺便翻译分享~等学习的时间充足些再写写自己的一些学习内容和知识点分析(最近有在接触的:复习(C#,SQL).(学习)T ...

随机推荐

  1. JavaEE Tutorials (15) - 对Java持久化API应用使用二级缓存

    15.1二级缓存概述190 15.1.1控制实体是否可以缓存19115.2指定缓存模式设置来提高性能192 15.2.1设置缓存获取和存储模式192 15.2.2通过编程方式控制二级缓存194

  2. Gartner 认定 Microsoft 为具有远见卓识的云基础结构即服务提供商

    四个月前, Windows Azure 基础结构服务结束了预览版阶段,正式发布了,它具有业内领先的 SLA.随后, 凭借愿景的完整性和执行力,Gartner 很快认可了 Microsoft 在市场中的 ...

  3. dataguard dubugs

    alter database open read only;alter database open read only*ERROR at line 1:ORA-10456: cannot open s ...

  4. 纯JavaScript实现HTML5 Canvas六种特效滤镜

    纯JavaScript实现HTML5 Canvas六种特效滤镜  小试牛刀,实现了六款简单常见HTML5 Canvas特效滤镜,并且封装成一个纯 JavaScript可调用的API文件gloomyfi ...

  5. nyist 202 红黑树(二叉树中序遍历)

    旋转对中序遍历没有影响,直接中序输出即可. #include <iostream> #include <cstdio> using namespace std; int n; ...

  6. 自己动手写处理器之第一阶段(3)——MIPS32指令集架构简单介绍

    将陆续上传本人写的新书<自己动手写处理器>(尚未出版).今天是第四篇.我尽量每周四篇 1.4 MIPS32指令集架构简单介绍 本书设计的处理器遵循MIPS32 Release 1架构,所以 ...

  7. linux之线程

    http://blog.csdn.net/lanyan822/article/details/7586845 POSIX线程数据类型: pthread_t 线程标识符: pthread_mutex_t ...

  8. nbtstat 查询IP地址对应的计算机名称

    使用命令nbtstat -a ipaddress即可,例如:nbtstat -a 192.168.1.2.

  9. 2015年最棒的10个 JavaScript 框架

    JavaScript是最流行的前端开发程序设计语言.它为WEB开发者提供了能够设计出具有丰富功能.干净用户界面的WEB应用的能力.JavaScript框架使得WEB应用的设计变的简单,并且它能够提供很 ...

  10. Problem C Andy's First Dictionary(set的使用)

    题目链接:Problem C 题意:输入一个文本,找出所有不同的单词,按照字典序从小到大输出,单词不区分大小写. 思路:将字母序列都存为小写,非字母的字符变成空格,然后利用stringstream实现 ...