C++版算法分析小技巧

来源:互联网 发布:淘宝网摩托车专卖区 编辑:程序博客网 时间:2024/04/28 23:12

    在算法分析过程中,我们往往有这样的两个需求:一、我们需要一些随机生成的测试数据。二、我们需要测试算法运行的时间。这就需要我们在编程上采用一些小技巧来满足这两个需求。

    一、随机生成测试数据

        测试数据的含义非常广泛,可以是简单的整型数值,也可以是对象等,由于我所研究的算法比较简单,所以能满足给出[a,b)内的随机整型数据就可以了。

        随机数据的生成涉及到两个函数rand与srand这两函数都包含在<iostream>头文件中。

        我们先来看看srand函数,它的原型是

void srand(   unsigned int seed );

        可以看出srand函数没有返回值,参数是一个无符号整型数seed,seed即为随机数的种子,srand这个函数就是用来设置随机数种子的,何为随机数种子?这就要我们理解随机数的产生机制,编程中的随机数与我们生活中的随机过程中产生的随机数不同,为伪随机数,即程序产生的随机数是由一些公式计算得来的,这些公式依据变量的值计算出随机数,这里被这些公式所依赖的变量就叫做随机数种子,意为随机数是根据这些种子数计算出来的一些整型序列,当种子相同的时候,这个序列相同。

        接下来我们来看看rand函数这个函数的原型是

int rand( void );

        根据msdn上remark,rand函数的返回值为0到RAND_MAX(32767),所以调用rand函数会返回一个0到32767之间的随机值,问题来了,如果只需要[a,b)数怎么办呢?只需要rand()%(b-a)+a即可,想想为什么。

        到现在为止,我们已经可以随意地获取任意区间内的整型随机数了,但是如果直接调用rand没有设置任何种子也是可以出结果的,难道没有种子变量也可以计算么?

        回过头来看看srand函数在msdn上的remark:

            The srand function sets the starting point for generating a series of pseudorandom integers in the current thread. To reinitialize the generator to create the same sequence of results, call the srand function and use the same seed argument again. Any other value for seed sets the generator to a different starting point in the pseudorandom sequence. rand retrieves the pseudorandom numbers that are generated. Calling rand before any call to srand generates the same sequence as calling srand with seed passed as 1.

        这段话的大意是rand函数每次调用时都会去检查随机种子,如果没有在所有rand函数调用前都没有设置随机种子,随机种子默认值为1,相当于默认调用了一次srand(1)。

        所以此时生成[a,b)之间的整型随机数代码如下:

/************************************************************ * (C) Avery Liu HIT,2013 * NOTE: *      This is the script of csdn blog demo codes, written * by Avery Liu. you can copy and run these codes freely, but  * no commercial activity is allowed.  *      If you want to use these code for other usage, please  * indicates the source:      http://blog.csdn.net/avery_liu ***********************************************************//*srand & rand demo*/#include<iostream>using namespace std;#define MAX_TEST_NUM 100int myRand(int lowBound, int upBound);int main(){int lowBound, upBound;cin >> lowBound >> upBound;for (int i = 0; i < MAX_TEST_NUM; i++){int returnVal = myRand(lowBound, upBound);if (returnVal == -1){cout << "Error Input" << endl;break;}cout << returnVal << endl;}system("Pause");return 0;}int myRand(int lowBound, int upBound){/*Check the parameters*/if (lowBound >= upBound) return -1;else if (upBound < 1) return -1;return rand() % (upBound - lowBound) + lowBound;}

    二、测试算法运行时间

        测试运行时间的方法有很多种,在这里我就先举一例使用函数GetTickCount来测试运行时间的。

        先来看看GetTickCount,它的原型是:

DWORD WINAPI GetTickCount(void);

        DWORD是Windows定义的unsigned long型的变量,GetTickCount返回的是系统开机运行到现在的时间,但最高只能表示到49.7天,如果超过这个数值的话又会归零。为什么呢?由于是32位无符号长整型,所以它表示的最大范围是(1>>32)-1,换算后即为49.7103天。超过这个数值按照我们计算机组成原理中数的表示的知识,11...11(总共32个)就会变成00...00(32个0),也就是说归回零点重新计数了,怎么解决这个问题呢?使用函数GetTickCount64,就可以了它的返回值是ULONGLONG,也就是unsigned long long,为64为无符号整型,最大可表示范围为:(1>>64)-1,也就是2.13504*(10^11)天,这是一个什么概念?算一算大概是5.84942亿年,够大了吧(*^__^*) 。

        这里可以看出这个GetTickCount函数在一般情况下是可以应用的,但是有些倒霉的人正好是在49.7的交界处调用了这个函数的话返回错误的结果,所以如果你刚开机,那就大无畏地用它吧,如果像我一样经常盖上笔记本就不管的,那还是用GetTickCount64吧。

        要注意这两个函数都需要头文件<Windows.h>,并且GetTickCount在Windows 2000以后的系统才能调用,GetTickCount64在Windows Server 2008和Windows Vista之后的系统才能调用。

        具体用法如下:

/************************************************************ * (C) Avery Liu HIT,2013 * NOTE: *      This is the script of csdn blog demo codes, written * by Avery Liu. you can copy and run these codes freely, but  * no commercial activity is allowed.  *      If you want to use these code for other usage, please  * indicates the source:      http://blog.csdn.net/avery_liu ***********************************************************//*srand & rand demo*/#include<iostream>#include<Windows.h>using namespace std;#define MAX_TEST_NUM 10000int myRand(int lowBound, int upBound);int main(){int lowBound, upBound;cin >> lowBound >> upBound;ULONGLONG startTime = GetTickCount64();for (int i = 0; i < MAX_TEST_NUM; i++){int returnVal = myRand(lowBound, upBound);if (returnVal == -1){cout << "Error Input" << endl;break;}cout << returnVal << endl;}ULONGLONG endTime = GetTickCount64();//ULONGLONG test = 0 - 1;//cout << test/(24*3600000.0) << endl;cout << "time: " << (endTime - startTime) / 1000.0<< endl;system("Pause");return 0;}int myRand(int lowBound, int upBound){/*Check the parameters*/if (lowBound >= upBound) return -1;else if (upBound < 1) return -1;return rand() % (upBound - lowBound) + lowBound;}
        只要在你想测试时间的代码段前定义一个ULONGLONG startTime并调用GetTickCount获得时间,再在代码段后调用获得endTime,endTime-startTime再进行转换后就是我们所需要的算法运行时间。

        中间被注释掉的是我用来测试ULONGLONG的最大表示范围的,大家可以不必理会。


        随机数生成和测试运行时间这两部分还有可以改进的地方,比如随机数种子可以设置成当前系统时间,但要注意要保留下这个种子,不然如果出现问题了就难以找到让程序出错的输入,获取运行时间还有很多方法,比如CTime类啊,有时间再做比较。

        如有任何问题,欢迎大家指正交流。