数据结构与算法分析 C++描述(第3版) 习题2.8 详尽分析

数据结构与算法分析 C++描述(第3版) 习题2.8 详尽分析

Data Structures and Algorithm Analysis In C++ Third Edition By Mark Allen Weiss 转载请注明出处及作者:九天雁翎

a:因为12很明显,所以不证明了。

至于算法3,可以用数学归纳法证明,x详细证明如下:

1.n=1时,a[0]=1,都是100%,成立;

2.n=2时,

for(i = 1; i < n; ++i)

swap (a[i],a[ randInt(0,i)]);

第一次循环,当i=1时,此时a[0]=1,a[i]=a[1]

randInt(0,1)50%机会为050%机会为1,所以,swap(a[1],a[0])的机会为50%,即,序列为0,1; 1,0几率都为50%。 成立

3. n=3时,第二次循环时,当i=2时,此时有两种可能,原序列为0,1; 1,0的几率各为50%randInt(0,2)可能为0,1,2的几率各位1/3.此时,原序列为0,1,randInt(0,2)0,那么此序列经过swap(a[2],a[0]),最后序列为2,1,0,此序列的可能性为(1/2)*(1/3)=1/6; 同理易得,最后此序列为

210, 021, 012, 201, 120, 102的几率各位1/6,而此序列的所有排列可能为= 3 * 2 = 6,所以 此时成立.

4.假设此命题在n = k时成立,那么就是说,k前面序列共有序列种,且所有序列的几率为1/.

5.n=k+1,k+1次循环时,前面序列正好为n=k时的情况,此时i = k. randInt(0,k)共可能为0~k,k+1种可能。所以以前的每个可能序列在置换后有k+1种可能,而以前共有k种等可能序列,那么最后可能的序列为k*(k+1)种可能,并且,因为原序列为等可能的,每个等可能序列产生的序列都是k+1种,所以最后的序列也是等可能的。而当n=k+1时,应该共有=(k+1)*k种,所以,此命题得证。

即算法3产生的也是等置换序列。

这里最后要说明的是,证明时默认地利用了一个命题,

当原序列为互不相等的等可能序列时,新加入一个与原来序列任何数值都不相等的数值,无论这个数值放在原序列的哪个位置,都不可能使原不相等的序列相等,说的好复杂啊-_-!

基本就是这样,210, 021, 012, 201, 120, 102,这是你加入一个新的数值3,无论你把3插入序列的哪个位置,因为原来序列的排列不相等,所以,他们还是不会相等,这样,才保证了最后k个序列的k+1种可能都不相等,不会重复。

算法分析答案说的很详细,可以参考答案,即第一个算法为O(),第二个算法为ONlogN,第三个为线性。

实际算法的实现如下:

#include "jtianling/jtianling.h"

#include "boost/dynamic_bitset.hpp"

using namespace std;

//2.8题目中假设存在的随机数生成器

//根据其定义,此算法生成从ij的随机数,而且包括边界i,j.i=

int randInt(int i, int j)

{

//确保i不然就交换

if(i > j)

{

int temp = i;

i = j;

j = temp;

}

//确保范围正确

return rand()%(j-i+1) + i;

}

//算法,算法描述如题目.8所示,根据习惯,不对arr[]的大小做任何检验

//调用此函数的人应该确保这一点,arr的大小大于等于n

void RandArrayCreate1(int arr[], int n)

{

int temp;

for(int i=0; i

{

while(true)

{

temp = randInt(1,n);

int j = 0;

while(j

{

if(arr[j] == temp){ break;}

++j;

}

if(j == i)

{

arr[j] = temp;

break;

}

}

}

}

//算法,算法描述如题目.8所示,根据习惯,不对arr[]的大小做任何检验

//调用此函数的人应该确保这一点,arr的大小大于等于n

void RandArrayCreate2(int arr[], int n)

{

//为了正好与数值一一对应,浪费位算了,默认就都为flase

bool *used = new bool[n+1];

memset(used, 0, sizeof(bool) * (n+1));

int temp;

for(int i=0; i

{

while(true)

{

temp = randInt(1,n);

if(used[temp] == false)

{

arr[i] = temp;

used[temp] = true;

break;

}

else

{

continue;

}

}

}

delete []used;

}

//算法,算法描述如题目.8所示,根据习惯,不对arr[]的大小做任何检验

//调用此函数的人应该确保这一点,arr的大小大于等于n

void RandArrayCreate3(int arr[], int n)

{

for(int i=0; i

{

arr[i] = i + 1;

}

for(int i=0; i

{

swap(arr[i], arr[randInt(0, i)] );

}

}

void RandArrayCreate2a(int arr[], int n)

{

//为了正好与数值一一对应,浪费位算了,默认就都为flase

boost::dynamic_bitset<> used(n+1);

int temp;

for(int i=0; i

{

while(true)

{

temp = randInt(1,n);

if(used[temp] == false)

{

arr[i] = temp;

used.set(temp);

break;

}

else

{

continue;

}

}

}

}

void RandArrayCreate2b(int arr[], int n)

{

//为了正好与数值一一对应,浪费位算了,默认就都为flase

vector<bool> used(n+1);

int temp;

for(int i=0; i

{

while(true)

{

temp = randInt(1,n);

if(used[temp] == false)

{

arr[i] = temp;

used[temp] = true;

break;

}

else

{

continue;

}

}

}

}

这里对算法的测试用另外一个测试函数来进行:

//因为这里主要是比较不同算法的实现,所以这里不区分算法及其实现,都称为算法

//比较算法的函数,其中算法函数的参数为数组(array)和整数(int)

//简写为AI,为了防止混乱,这里我没有使用函数的重载

//准备以后有新的不同参数的算法时再加入新的比较函数

//比较函数的参数解释如下:

//第一参数:各算法的用vector<>表示的函数指针数组

//第二参数:vector表示的一系列算法函数第二参数,用以做算法增长比较

//主要注意的是,这里的第二参数实际也是算法函数第一参数数组的大小

void CompareAlgorithmAI(const std::vector &pfAIVec,const std::vector<int>& vec)

{

double timeInAll = GetTime(); //这里保存所有比较花费的时间

std::ofstream out("CompareAlogrithmAIResult.txt");//将结果保存在这里

//在文件中输入第一行

out <<"parameter" <<'/t';

for(int i=0; i

{

out <<"Algorithm " <'/t';

}

out <

double timeTaken; //每个算法一次消耗的时间

//以算法参数的数组个数为循环条件,每个数值让各个算法分别调用

for(int i=0; i

{

out <'/t';

int *arr = new int[ vec[i] ]; //每次动态生成一个数组

for(int j=0; j

{

timeTaken = GetTime();

pfAIVec[j](arr, vec[i]); //实际调用不同算法

timeTaken = GetTime() - timeTaken;

out <'/t';

}

delete []arr; //删除arr,因为只考察算法的效率,所以根本不关心结果

//至于各算法是否正确由调用者保证

out<

}

timeInAll = GetTime() - timeInAll;

out <<"Compared time in all: " <

}

最后得到的结果用excel打开,实在是更加赏心悦目而且更好的可以保存下来,可以只测试一个,也可以一次测试一组,以后的算法比较中还可以重复的利用,这里利用了vector来传递参数,有利于减少参数的数量。关于GetTime的含义,请参考置顶文章

parameter

RandArrayCreate1

parameter

RandArrayCreate3

250

6.62E-04

100000

1.85E-02

500

4.61E-03

200000

3.78E-02

1000

1.59E-02

400000

5.83E-02

2000

6.70E-02

800000

1.17E-01

Compared time in all: 8.9593e-002

1600000

2.30E-01

3200000

4.61E-01

6400000

9.22E-01

Compared time in all: 1.9196e+000

parameter

RandArrayCreate2

RandArrayCreate2a

RandArrayCreate2b

250

1.30E-04

7.51E-04

1.03E-02

500

2.72E-04

1.64E-03

1.88E-02

1000

6.78E-04

3.49E-03

5.65E-02

2000

1.41E-03

9.19E-03

1.11E-01

4000

2.70E-03

1.47E-02

2.63E-01

8000

5.97E-03

3.84E-02

4.66E-01

16000

1.31E-02

7.39E-02

1.33E+00

32000

3.60E-02

1.91E-01

2.39E+00

Compared time in all: 5.0447e+000

很显然的结果,不多说了,不过书中给出要我运行的第2算法的参数实在是太大了,估计一天都运行不完,这里也可以看到,在动态bool位使用上,直接New最快,用Boost库的动态bit是其次,用标准库的vector只能用慢的出奇来形容了,当然,其实很多时候都是同样的结论,因为用new毕竟不是那么好管理,但是不需要建构析构,而用了类来管理,自然需要更多的时间资源,但是更好管理并且动态更改起来更加方便。这需要自己去权衡利弊了。

至于最E,最坏情况的分析,前两个在一直随机到重复数字的时候可以到无限大。。。汗!第三个算法是线性的,也无所谓最坏情况。

你可能感兴趣的:(数据结构与算法分析 C++描述(第3版) 习题2.8 详尽分析)