我们热爱生命科学!-生物行

[注意] cc++教程(2)

时间:2006-03-01 08:55来源:Internet 作者:bioguider 点击: 693次

  方法4:

printf("2,");
for(i=3; i<100; i+=2)
{
 int k = sqrt(i);
 for(j=3; j<= k; j+=2)
  if(i%j == 0) break;
  if(j >= k ) printf("%d", i);
}

  然后,开始证明为什么我们判断素数的时候,只需要验算到平方根就足够了:

  假设p是合数,那么令:p=a*b。反正法:由于我们已经判断了p的平方根以内的整数都不能被p整除,于是 a>SQRT(p)。基于同样的理由 b>SQRT(p)。于是 p = a * b > SQRT(p) * SQRT(p) = p 得出矛盾, 命题得正。

  的确,“方法4”的确比“方法1”的运行速度要提高了好几倍,甚至好几十倍。但我们仔细分析测试看看。

  (1)“程序4”到底比“程序1”快了多少那?我在某台计算机上进行测试(P4,1.5G)得到的速度对比表:

计算范围 100 1000 10000 100000
速度差 0.00秒 0.01秒 0.18秒 15秒


  
  (2) 在10万以上,才会看出一些差别。而这种差别根本就不够底偿程序设计阶段的付出。如果计算的范围再大,那么不管是“方法1”,还是“方法4”都不是好的算法。(计算素数的另外一个比较优秀的算法叫“漏筛法”)

  (3)写出“方法1”,只要具有小学四年级的数学水平就够了,而“方法4”则需要初中三年级的水平并且还要具备一些“数论”的知识。

  (4)从维护性看,如果你写的程序需要另外一个程序员来维护,或者若干时间以后,你重新来阅读这段程序,那么就会对这个程序产生很多疑问:这个求平方根是干什么用的?其实,就这个题目来说,使用到“方法3”就已经足够了。

  总结发言:

  I. 计算机的价格每年下降一半,而运算速度每年提高一倍”,因此我们应该把速度提高的任务交给硬件实现。

  II. 从易读性、维护性出发,程序员只负责按定义给出软件实现。算法的问题是数学家解决的。

  题外话:

  多年以来,人们一直在寻找动态图象(影视)的存储和回放的算法,但效果都不理想。直到有人发现,原来在200多年前的数学家早就帮我们解决了这个问题——傅立叶(Fourier)级数展开。因此我要说,优秀的算法不是我们程序员要考虑的问题,我们的任务只要按照数学家给出的算法翻译为计算机程序语言而已。(这句话恐怕要遭到大多数程序员抛出的板砖袭击)再比如,计算一元多次方程解的问题。我们使用的就是牛顿的迭代算法。不要怪我瞧不起你,你能发明这个方法的话,那就是当代的牛顿了。

  四、程序的易读性与书写方法

  程序是否容易阅读和维护,与怎么书写有很大的关系。说实在的,C语言中为了方便程序员书写,允许使用++,--,<<,&&,?......这些运算符号。但很多人经常乱用,以为自己写的程序多么简洁,效率多高。其实,当你分行书写的话则更加容易阅读和维护,效率也不会降低,因为编译程序早就帮你优化为最快捷的代码了。先看一个简单的例子:

  计算一个整数乘 255(C语言)

  方法1:a *= 255;

  方法2:因为移位运算比乘法运算要快很多倍,因此a*255的运算书写为:

a =(a<<8)-a; //a*255 = a*256 - a = (a<<8) - a

  方法1的书写非常简单,直截了当,显然更容易维护。而方法2的书写运用了移位的技巧,不容易阅读,但效率最高。是不是真的是这样那?把这两个程序编译为汇编代码看看。原来无论是方法1还是方法2,它们的汇编代码都是一样的:

mov ecx, eax
shl eax, 8
sub eax, ecx

  也就是说,你认为非常技巧的书写方法,其实编译器的优化功能早就帮你想到了。那么方法2的方式就很值得批判了。下面是几个有关C语言书写方面的重要原则:
 
  1)尽量表达愿义,多加注释;

  2)变量名称和函数名称,要使用有意义的符号,并且遵守“匈牙利命名法”;

  3)不要为俭省内存,使一个变量在一个模块中表达多个含义。

  4)在某个模块中,前半部分用i表示计数器,由于后半部分不再使用计数器了,于是又用i来保存某个中间的结果。等你维护这段程序的时候,保证你肯定会犯傻的。

  5)在使用条件表达式的时候,不要混合书写运算表达式;

  经常有人在书写for循环的时候,使用这样的方式: for(int a=1,s=0; a<=100 && (s+=a); a++);

  天呀,这样写是不会提高程序运行效率的,尤其是当运算表达式复杂的时候,就更不容易阅读了,还是把运算写到for的循环体中吧。

int s = 0;
for(int a=1; a<=100; a++)
 s += a; //计算1+2+...+100 这不很好吗?!

  再比如,if(a=b)这个写法在语法上是允许的,但不要使用。要使用也要if(0!=(a=b))这样的方式。 还有值得一提的是慎用“,”(逗号运算符)。

  不要连续使用++,--,<<,*,& .....这样的运算符号。

  a = b++-(--c<<1+e&0x0f>>1); //这个人有病。出这个题目考试的老师,也有病。

  常量要写在条件表达式的左边;

  if(5 == a) 这是正确的写法,这样书写可以避免勿输入而导致的 if(a=5)这样的错误。

  避免程序中{ }的嵌套层次太深;

  最多4层。如果必须大于4层,那么写成调用子函数或宏的方式。

  尽量多地使用断言;

  当你在书写程序的过程中,凭你的智慧,你一定是知道:程序运行到我正书写的这行代码的时候某个变量一定是某个值。好啦,那么不要忧郁,马上加上一句代码:ASSERT(nnn == xxx);。将来在调式维护这段代码的时候,你会得到无限美妙的回报。

  书写需要“成对匹配”使用的代码的时候,在写使用代码之前,就先把结束写出来;

file.Open(...); //当要打开文件的时候 char *lp=new char [100]; //当要申请内存的时候
...... //先不要写这段代码 ...... //先不要写这段代码
file.Close(); //马上写关闭 delete [] lp; //马上写释放

xxx.Loack(); //当某个对象需要锁定的时候 for(....)
...... //先不要写这段代码 { //写大括号的时候
xxx.Unlock(); //马上写解锁 } //马上写大括号结束

  和这个道理相同,在C++的类中,如果需要申请内存,那么先在构造函数中给出 lp=NULL;然后马上在析构函数中书写 if(lp) delete []lp; (责任编辑:泉水)

顶一下
(9)
100%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片