方法4:
printf("2,");
然后,开始证明为什么我们判断素数的时候,只需要验算到平方根就足够了: 假设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
(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
也就是说,你认为非常技巧的书写方法,其实编译器的优化功能早就帮你想到了。那么方法2的方式就很值得批判了。下面是几个有关C语言书写方面的重要原则: 2)变量名称和函数名称,要使用有意义的符号,并且遵守“匈牙利命名法”; 3)不要为俭省内存,使一个变量在一个模块中表达多个含义。 4)在某个模块中,前半部分用i表示计数器,由于后半部分不再使用计数器了,于是又用i来保存某个中间的结果。等你维护这段程序的时候,保证你肯定会犯傻的。 5)在使用条件表达式的时候,不要混合书写运算表达式; 经常有人在书写for循环的时候,使用这样的方式: for(int a=1,s=0; a<=100 && (s+=a); a++); 天呀,这样写是不会提高程序运行效率的,尤其是当运算表达式复杂的时候,就更不容易阅读了,还是把运算写到for的循环体中吧。
int s = 0;
再比如,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]; //当要申请内存的时候 xxx.Loack(); //当某个对象需要锁定的时候 for(....)
和这个道理相同,在C++的类中,如果需要申请内存,那么先在构造函数中给出 lp=NULL;然后马上在析构函数中书写 if(lp) delete []lp; (责任编辑:泉水) |