编程珠玑<续>之性能监视工具3-验证篇
2014-03-18 17:41
295 查看
今天还是继续讨论第一章(这效率-_-|||)。其实已经看到第三章了,只不过看到后面越发觉得不太对劲,这书不该这么看啊,怎么能一过了之呢?太对不起作者了好吧(虽然这本书很薄看起来很容易看完的样子)。更何况,而且明明很多都没有弄懂,于是有了现在这个验证篇。
1.利用gcov对书中素数例子进行验证
尽管知道书上说的肯定是对的,可是还是想自己动手试试看结果。
首先,编译 加入gcov的参数
运行prime后 ,代码覆盖率的测试数据已经收集在了gcda文件中,再用gcov 生成结果。
P1 简单版本
P2 只检验平方根
P3只计算一次开方
由于3/4的合数都包含了2,3,5,因此特殊考虑。不过下面这个程序有问题
以上程序存在问题,没有将素数:2,3,5检查出来 ,正确检验:
P6 用乘法替代开方
习题2 中利用埃氏筛选法,n这里我取的还是1000为了和上面的做比较
2.求解习题1 (很有意思的一道题, 我可以说我之所以想写这篇文章主要是为了这道题。)
题目:假设数组X[1...1000]中散布着随机实数。下面这个例程计算最小和最大值。
B.C.Dull 先生注意到,如果一个元素是新的最大值,则这个元素不可能是最小值。因而他把两次比较写成
我当然没有猜到答案,看了书上给的答案后还是觉得不太清楚,可能还是和翻译有关吧。然后,自己先动手验证了一番。
我也在N=1000的条件下将程序运行了15遍,排序后的赋值数依次为:
3 4 4 5 5 6 7 7 7 7 7 8 9 9 9
我这里的结果是6.47,果然和理论给的期望值6.485很接近。
那么到底为什么答案是这样呢?
书上是这么说的,knuth在The Art of Computer Programming,Volume1:Foundamental Algorthms的1.2.10中说明了该算法在平均情况下少进行Hn-1次赋值,其中
Hn=1+1/2+1/3+...+1/n,是一个调和级数。而该调和级数对于特定的n值收敛于ln(n)+r,r是欧拉常数约等于0.577。于是有 ln(1000)+0.577-1 =6.485。
也就是说比较1000的情况下,平均省7次左右。
究竟为什么是调和级数呢?我没有看那本书,但网上关于这个题的说法不多,有个听起来有点意思,说的是减少的次数等于第一个条件命中的次数(?)。第n个数为最大值的概率是1/n,那么所有第一个条件命中的次数概率和为:1/n+1/(n-1)+...+1/1=ln(n)+r。是这样么?
还有一个人用了更为严谨的证明如下:
令随机变量Xi对应于Ai>MAX这个事件有:Xi={1 如果 Ai>MAX ,0 如果Ai<MAX}
令随机变量X表示少比较的总次数
X=X2+X2+...+Xn
问题转化为求X的期望值
因为A是随机排列,Ai是前i个值中最大值的概率为1/i
E(Xi)=1*(1/i)+0*(1-1/i)=1/i
E(X)=E[X2+X2+...+Xn]=E(X2)+E(X3)+E(X4)+...+E(Xn) =1/2+1/3+...+1/n=ln(n)+O(1)
1.利用gcov对书中素数例子进行验证
尽管知道书上说的肯定是对的,可是还是想自己动手试试看结果。
首先,编译 加入gcov的参数
g++ prime.cpp -fprofie-arcs -ftest-coverage -o prime
运行prime后 ,代码覆盖率的测试数据已经收集在了gcda文件中,再用gcov 生成结果。
gcov prime下面是书上关于素数几个例子的验证。
P1 简单版本
999: 7:int prime(int n){ 999: 8: int i; 78190: 9: for( i=2;i<n;i++){ 78022: 10: if(n%i==0){ 831: 11: return 0; -: 12: }} 168: 14: return 1;}
P2 只检验平方根
5456: 6:int root(int n){ //被调用5456次和for循环次数一样 5456: 7: return (int) sqrt((float)n); -: 8:} 999: 10:int prime(int n){ 999: 11: int i; 5456: 12: for( i=2;i<=root(n);i++){ 5288: 13: if(n%i==0){ 831: 14: return 0; -: 15: }} 168: 17: return 1;} -: 23:int main() 1: 24:{ 1: 25: int i,n; 1: 26: n=1000; 1000: 27: for(i = 2; i <= n; i++) 999: 28: if (prime(i)) 168: 29: cout<<i<<endl; 1: 31:return 0; 3: 32:}
P3只计算一次开方
999: 6:int root(int n){ 999: 7: return (int) sqrt((float)n); -: 8:} 999: 11:int prime(int n){ 999: 12: int i,bound;//用bound存储root开方结果,减少root被调用次数,这里root在被调用999次 999: 13: bound= root(n); 5456: 14: for( i=2;i<=bound;i++){ 5288: 15: if(n%i==0){ 831: 16: return 0; -: 17: }} 168: 19: return 1;} -: 24:int main() 1: 25:{ int i,n; 1: 27: n=1000; 1000: 28: for(i = 2; i <= n; i++) 999: 29: if (prime(i)) { 168: 31: cout<<i<<endl; } 1: 34:return 0; 3: 35:}P4 特殊情况 2、3、5
由于3/4的合数都包含了2,3,5,因此特殊考虑。不过下面这个程序有问题
265: 6:int root(int n){ 265: 7: return (int) sqrt((float)n); -: 8:} 999: 12:int prime(int n){ 999: 13: int i,bound; 999: 15: if(n%2==0) return 0; 499: 16: if(n%3==0) return 0; 332: 17: if(n%5==0) return 0; -: 18: 265: 19: bound= root(n); 1695: 20: for( i=7;i<=bound;i = i+2){ 1530: 21: if(n%i==0){ 100: 22: return 0; -: 23: }} 165: 25: return 1; -: 26:} -: 27: -: 30:int main() 1: 31:{ int i,n; 1: 33: n=1000; 1000: 34: for(i = 2; i <= n; i++) 999: 35: if (prime(i)) { 165: 36: cout<<i<<endl; -: 37: } 1: 38:return 0; 3: 39:}
以上程序存在问题,没有将素数:2,3,5检查出来 ,正确检验:
999: 11:int prime(int n){ 999: 12: int i,bound; -: 13: 999: 14: if(n%2==0) return (n==2); 499: 15: if(n%3==0) return (n==3); 332: 16: if(n%5==0) return (n==5); -: 17: 1695: 18: for( i=7;i*i<=n;i = i+2){ 1530: 19: if(n%i==0){ 100: 20: return 0; -: 21: }} 165: 23: return 1; -: 24:} -: 25: -: 27:int main() 1: 28:{ 1: 29: int i,n; 1: 30: n=1000; 1000: 31: for(i = 2; i <= n; i++) 999: 32: if (prime(i)) { 168: 33: cout<<i<<endl; //上面这里是165次,没有检验2,3,5,这里将这个三个数也考虑进来了 -: 34: } 1: 36:return 0; 3: 37:}
P6 用乘法替代开方
for( i=7;i*i<=n;i = i+2)速度加快了。
习题2 中利用埃氏筛选法,n这里我取的还是1000为了和上面的做比较
1: 6:int main(){ 1: 7: int i, p, n; 1: 8:char x[1002]; 1: 10: n = 1000; -: 11: 1001: 12:for (i = 1; i <= n; i++) 1000: 14: x[i] = 1; 1: 15: x[1] = 0; 1: 16: x[n+1] = 1; 1: 18: p = 2; 169: 20: while (p <= n) { 168: 21: cout<<p<<" "; 2126: 23: for (i = 2*p; i <= n; i = i+p) 1958: 25: x[i] = 0; 999: 27: do 999: 28: p++; -: 30: while (x[p] == 0); -: 31: }
2.求解习题1 (很有意思的一道题, 我可以说我之所以想写这篇文章主要是为了这道题。)
题目:假设数组X[1...1000]中散布着随机实数。下面这个例程计算最小和最大值。
Max:= Min:=X[1] for I:=2 to 1000 do if X[I] >Max then Max := X[I] if X[I]<Min then Min := X[I]
B.C.Dull 先生注意到,如果一个元素是新的最大值,则这个元素不可能是最小值。因而他把两次比较写成
if X[I] >Max then Max := X[I] else if X[I]<Min then Min := X[I]这样平均起来将节省多少次比较?先猜答案,再通过实现和监控程序性能功能来找出答案。你猜的怎么样?
我当然没有猜到答案,看了书上给的答案后还是觉得不太清楚,可能还是和翻译有关吧。然后,自己先动手验证了一番。
-: 1:#include<stdio.h> -: 2:#include<stdlib.h> -: 3:#include<iostream> -: 4:#include<time.h> -: 5:#define random() (rand()/(double)(RAND_MAX)) -: 6:using namespace std; -: 7: 1: 8:void MinMax(double a[],int n){ 1: 9: double max=a[0],min=a[0]; 1000: 10: for(int i=1;i<n;i++){ 999: 11: if(a[i]>max) max=a[i]; 999: 12: if(a[i]<min) min=a[i]; //999 -: 13: } 1: 14: cout<<"original min:"<<min<<"original max:"<<max<<endl; -: 15:} -: 16: 1: 17:void MinMax2(double a[],int n){ 1: 18: double max=a[0],min=a[0]; 1000: 19: for(int i=1;i<n;i++){ 999: 20: if(a[i]>max) max=a[i]; 995: 21: else if(a[i]<min) min=a[i];} //995 1: 23: cout<<"Optimized min:"<<min<<"original max:"<<max<<endl; -: 24:} -: 25: 1: 26:int main(){ 1: 27: int n=1000; 1: 28: double a[1000]; 1: 29: srand((int)time(0)); 1001: 30: for(int i=0;i<n;i++){ 1000: 31: a[i]=rand();} 1: 33: MinMax(a,n); 1: 34: MinMax2(a,n); 1: 35:return 0; 3: 36:}
我也在N=1000的条件下将程序运行了15遍,排序后的赋值数依次为:
3 4 4 5 5 6 7 7 7 7 7 8 9 9 9
我这里的结果是6.47,果然和理论给的期望值6.485很接近。
那么到底为什么答案是这样呢?
书上是这么说的,knuth在The Art of Computer Programming,Volume1:Foundamental Algorthms的1.2.10中说明了该算法在平均情况下少进行Hn-1次赋值,其中
Hn=1+1/2+1/3+...+1/n,是一个调和级数。而该调和级数对于特定的n值收敛于ln(n)+r,r是欧拉常数约等于0.577。于是有 ln(1000)+0.577-1 =6.485。
也就是说比较1000的情况下,平均省7次左右。
究竟为什么是调和级数呢?我没有看那本书,但网上关于这个题的说法不多,有个听起来有点意思,说的是减少的次数等于第一个条件命中的次数(?)。第n个数为最大值的概率是1/n,那么所有第一个条件命中的次数概率和为:1/n+1/(n-1)+...+1/1=ln(n)+r。是这样么?
还有一个人用了更为严谨的证明如下:
令随机变量Xi对应于Ai>MAX这个事件有:Xi={1 如果 Ai>MAX ,0 如果Ai<MAX}
令随机变量X表示少比较的总次数
X=X2+X2+...+Xn
问题转化为求X的期望值
因为A是随机排列,Ai是前i个值中最大值的概率为1/i
E(Xi)=1*(1/i)+0*(1-1/i)=1/i
E(X)=E[X2+X2+...+Xn]=E(X2)+E(X3)+E(X4)+...+E(Xn) =1/2+1/3+...+1/n=ln(n)+O(1)
相关文章推荐
- 编程珠玑<续>之性能监视工具2工具篇
- 编程珠玑<续>之性能监视工具
- 品味性能之道<五>:SQL分析工具
- 品味性能之道<六>:图形化SQL分析工具
- .NET泛型编程 性能提升工具 List<T>
- <2012 12 02> linux下利用valgrind工具进行内存泄露检测和性能分析
- iOS- 三步快速集成社交化分享工具ShareSDK + iOS- 无处不在,详解iOS集成第三方登录(SSO授权登录<无需密码>)
- <tx:method ... read-only="true" /> read-only提高查询性能的原因
- Mysql 5.6 在性能、可伸缩性、可用性方面的新突破<二>
- Arraylist LinkedList 区别 (读 <Java性能优化> 之List接口笔记 )
- Android性能优化之SpareArray<E>
- 性能优化:使用SparseArray代替HashMap<Integer,Object>
- BCC – 用于Linux性能监视,网络和更多的动态跟踪工具
- 验证视图状态 MAC 失败。如果此应用程序由网络场或群集承载,请确保 <machineKey> 配置指定了相同的 validationKey 和验证算法。不能在群集中使用 AutoGenerate。
- <转>全球100款大数据工具汇总
- Android开发工具——ADB(Android Debug Bridge) <三>DalvikVM之jdwp线程
- <Linux性能调优指南>主要思路流程
- Lazy<T>在Entity Framework中的性能优化实践(附源码)
- ToList<>()所带来的性能影响
- 黑马程序员--高新技术<一>eclipse开发工具及相关知识加强