编程珠玑<续>之性能监视工具3-验证篇

今天还是继续讨论第一章(这效率-_-|||)。其实已经看到第三章了,只不过看到后面越发觉得不太对劲,这书不该这么看啊,怎么能一过了之呢?太对不起作者了好吧(虽然这本书很薄看起来很容易看完的样子)。更何况,而且明明很多都没有弄懂,于是有了现在这个验证篇。

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)


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
优化这段代码<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>图书购物车</title> <style> </style> <script src="js/vue.js"></script> </head> <body> <div id="demo"> <table border="1"> <tr> <td></td> <td>书籍名称</td> <td>出版日期</td> <td>价格</td> <td>购买数量</td> <td>操作</td> </tr> <tr> <td></td> <td>{{books1.name}}</td> <td>{{books1.date}}</td> <td>¥{{books1.price}}</td> <td><button @click="down(books1)">-</button>{{books4.count}}<button @click="up(books1)">+</button></td> <td><button @click="del">移除</button></td> </tr> <tr> <td></td> <td>{{books2.name}}</td> <td>{{books2.date}}</td> <td>¥{{books2.price}}</td> <td> <button @click="down(books2)">-</button>{{books4.count}}<button @click="up(books2)">+</button> </td> <td><button @click="del">移除</button></td> </tr> <tr> <td></td> <td>{{books3.name}}</td> <td>{{books3.date}}</td> <td>¥{{books3.price}}</td> <td> <button @click="down(books3)">-</button>{{books4.count}}<button @click="up(books3)">+</button> </td> <td><button @click="del">移除</button></td> </tr> <tr> <td></td> <td>{{books4.name}}</td> <td>{{books4.date}}</td> <td>¥{{books4.price}}</td> <td> <button @click="down(books4)">-</button>{{books4.count}}<button @click="up(books4)">+</button> </td> <td><button @click="del">移除</button></td> </tr> </table> <div>总价: ¥{{sum}}</div> </div> <!-- js部分 --> <script> const vm = new Vue({ el: "#demo", data: { books1: { name: '《算法导论》', date: '2006-9', price: 85.00, count: 1 }, books2: { name: '《UNIX编程艺术》', date: '2006-2', price: 59.00, count: 1 }, books3: { name: '《编程珠玑》', date: '2008-10', price: 39.00, count: 1 }, books4: { name: '《代码大全》', date: '2006-3', price: 128.00, count: 1 } }, computed: { sum () { return this.books1.price * this.books1.count + this.books2.price * this.books2.count + this.books3.price * this.books3.count + this.books4.price * this.books4.count } }, methods: { down (books1) { this.books1.count = books1.count - 1; } } }) </script> </body> </html>
04-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值