8种算法分析.docx

上传人:b****6 文档编号:5918312 上传时间:2023-01-02 格式:DOCX 页数:41 大小:57.10KB
下载 相关 举报
8种算法分析.docx_第1页
第1页 / 共41页
8种算法分析.docx_第2页
第2页 / 共41页
8种算法分析.docx_第3页
第3页 / 共41页
8种算法分析.docx_第4页
第4页 / 共41页
8种算法分析.docx_第5页
第5页 / 共41页
点击查看更多>>
下载资源
资源描述

8种算法分析.docx

《8种算法分析.docx》由会员分享,可在线阅读,更多相关《8种算法分析.docx(41页珍藏版)》请在冰豆网上搜索。

8种算法分析.docx

8种算法分析

数据结构经典问题和算法分析

(一)-迭代法

来源:

  作者:

  2007-5-3021:

17:

53  字体:

[大中小]

一、迭代法  

 迭代法是用于求方程或方程组近似根的一种常用的算法设计方法。

设方程为f(x)=0,用某种数学方法导出等价的形式x=g(x),然后按以下步骤执行:

 

(1)   选一个方程的近似根,赋给变量x0; 

(2)   将x0的值保存于变量x1,然后计算g(x1),并将结果存于变量x0; 

(3)   当x0与x1的差的绝对值还小于指定的精度要求时,重复步骤

(2)的计算。

 

若方程有根,并且用上述方法计算出来的近似根序列收敛,则按上述方法求得的x0就认为是方程的根。

上述算法用C程序的形式表示为:

 

【算法】迭代法求方程的根 

{    x0=初始近似根; 

   do { 

      x1=x0; 

      x0=g(x1);   /*按特定的方程计算新的近似根*/ 

      } while ( fabs(x0-x1)>Epsilon); 

   printf(“方程的近似根是%f\n”,x0); 

字串5

迭代算法也常用于求方程组的根,令 

      X=(x0,x1,…,xn-1) 

设方程组为:

 

      xi=gi(X)      (I=0,1,…,n-1) 

则求方程组根的迭代算法可描述如下:

 

【算法】迭代法求方程组的根 

   {    for (i=0;i

         x[i]=初始近似根; 

      do { 

         for (i=0;i

            y[i]=x[i]; 

         for (i=0;i

            x[i]=gi(X); 

         for (delta=0.0,i=0;i

字串1

            if (fabs(y[i]-x[i])>delta)      delta=fabs(y[i]-x[i]); 

         } while (delta>Epsilon); 

      for (i=0;i

         printf(“变量x[%d]的近似根是 %f”,I,x[i]); 

      printf(“\n”); 

   } 

   具体使用迭代法求根时应注意以下两种可能发生的情况:

 

(1)   如果方程无解,算法求出的近似根序列就不会收敛,迭代过程会变成死循环,因此在使用迭代算法前应先考察方程是否有解,并在程序中对迭代的次数给予限制; 

(2)   方程虽然有解,但迭代公式选择不当,或迭代的初始近似根选择不合理,也会导致迭代失败。

 字串5

数据结构经典问题和算法分析

(二)穷举搜索法

来源:

  作者:

  2007-5-3021:

26:

51  字体:

[大中小]

二、穷举搜索法 

     穷举搜索法是对可能是解的众多候选解按某种顺序进行逐一枚举和检验,并从众找出那些符合要求的候选解作为问题的解。

 

【问题】   将A、B、C、D、E、F这六个变量排成如图所示的三角形,这六个变量分别取[1,6]上的整数,且均不相同。

求使三角形三条边上的变量之和相等的全部解。

如图就是一个解。

 

程序引入变量a、b、c、d、e、f,并让它们分别顺序取1至6的证书,在它们互不相同的条件下,测试由它们排成的如图所示的三角形三条边上的变量之和是否相等,如相等即为一种满足要求的排列,把它们输出。

当这些变量取尽所有的组合后,程序就可得到全部可能的解。

细节见下面的程序。

 

【程序1】 

# include  

void main() 

{   int a,b,c,d,e,f; 

   for (a=1;a<=6;a++)    

      for (b=1;b<=6;b++)      { 

         if (b==a)      continue; 

字串9

         for (c=1;c<=6;c++)      { 

            if (c==a)||(c==b)   continue; 

            for (d=1;d<=6;d++)      { 

               if (d==a)||(d==b)||(d==c)    continue; 

for (e=1;e<=6;e++)      { 

   if (e==a)||(e==b)||(e==c)||(e==d)    continue; 

f=21-(a+b+c+d+e); 

if ((a+b+c==c+d+e))&&(a+b+c==e+f+a))   { 

printf(“%6d,a); 

   printf(“%4d%4d”,b,f); 

   printf(“%2d%4d%4d”,c,d,e); 字串6

   scanf(“%*c”); 

                  } 

               } 

            } 

         } 

      } 

按穷举法编写的程序通常不能适应变化的情况。

如问题改成有9个变量排成三角形,每条边有4个变量的情况,程序的循环重数就要相应改变。

 

   对一组数穷尽所有排列,还有更直接的方法。

将一个排列看作一个长整数,则所有排列对应着一组整数。

将这组整数按从小到大的顺序排列排成一个整数,从对应最小的整数开始。

按数列的递增顺序逐一列举每个排列对应的每个整数,这能更有效地完成排列的穷举。

从一个排列找出对应数列的下一个排列可在当前排列的基础上作部分调整来实现。

倘若当前排列为1,2,4,6,5,3,并令其对应的长整数为124653。

要寻找比长整数124653更大的排列,可从该排列的最后一个数字顺序向前逐位考察,当发现排列中的某个数字比它前一个数字大时,如本例中的6比它的前一位数字4大,这说明还有对应更大整数的排列。

但为了顺序从小到大列举出所有的排列,不能立即调整得太大,如本例中将数字6与数字4交换得到的排列126453就不是排列124653的下一个排列。

为了得到排列124653的下一个排列,应从已经考察过的那部分数字中选出比数字大,但又是它们中最小的那一个数字,比如数字5,与数字4交换。

该数字也是从后向前考察过程中第一个比4大的数字。

5与4交换后,得到排列125643。

在前面数字1,2,5固定的情况下,还应选择对应最小整数的那个排列,为此还需将后面那部分数字的排列顺序颠倒,如将数字6,4,3的排列顺序颠倒,得到排列1,2,5,3,4,6,这才是排列1,2,4,6,5,3的下一个排列。

按以上想法编写的程序如下。

  

【程序2】 

# include  

# define SIDE_N   3 

# define LENGTH   3 

# define VARIABLES   6 

int A,B,C,D,E,F; 

int *pt[]={&A,&B,&C,&D,&E,&F}; 

int *side[SIDE_N][LENGTH]={&A,&B,&C,&C,&D,&E,&E,&F,&A}; 

int side_total[SIDE_N]; 

main{} 

{   int i,j,t,equal; 

   for (j=0;j

      *pt[j]=j+1; 

   while

(1) 

   {   for (i=0;i

      {   for (t=j=0;j

            t+=*side[i][j]; 

         side_total[i]=t; 

字串5

      } 

      for (equal=1,i=0;equal&&i

         if (side_total[i]!

=side_total[i+1]   equal=0; 

      if (equal) 

      {   for (i=1;i

            printf(“%4d”,*pt[i]); 

         printf(“\n”); 

         scanf(“%*c”); 

      } 

      for (j=VARIABLES-1;j>0;j--) 

         if (*pt[j]>*pt[j-1])   break; 

      if (j==0)   break; 

字串7

      for (i=VARIABLES-1;i>=j;i--) 

         if (*pt[i]>*pt[i-1])   break; 

      t=*pt[j-1];* pt[j-1] =* pt[i]; *pt[i]=t; 

      for (i=VARIABLES-1;i>j;i--,j++) 

      {   t=*pt[j]; *pt[j] =* pt[i]; *pt[i]=t;   } 

   } 

从上述问题解决的方法中,最重要的因素就是确定某种方法来确定所有的候选解。

下面再用一个示例来加以说明。

 

【问题】   背包问题 

问题描述:

有不同价值、不同重量的物品n件,求从这n件物品中选取一部分物品的选择方案,使选中物品的总重量不超过指定的限制重量,但选中物品的价值之和最大。

 

设n个物品的重量和价值分别存储于数组w[ ]和v[ ]中,限制重量为tw。

考虑一个n元组(x0,x1,…,xn-1),其中xi=0 表示第i个物品没有选取,而xi=1则表示第i个物品被选取。

显然这个n元组等价于一个选择方案。

用枚举法解决背包问题,需要枚举所有的选取方案,而根据上述方法,我们只要枚举所有的n元组,就可以得到问题的解。

 字串1

显然,每个分量取值为0或1的n元组的个数共为2n个。

而每个n元组其实对应了一个长度为n的二进制数,且这些二进制数的取值范围为0~2n-1。

因此,如果把0~2n-1分别转化为相应的二进制数,则可以得到我们所需要的2n个n元组。

 

【算法】 

maxv=0; 

for (i=0;i<2n;i++) 

{   B[0..n-1]=0; 

   把i转化为二进制数,存储于数组B中; 

   temp_w=0; 

   temp_v=0; 

   for (j=0;j

   {   if (B[j]==1) 

      {   temp_w=temp_w+w[j]; 

         temp_v=temp_v+v[j]; 

      } 

      if ((temp_w<=tw)&&(temp_v>maxv)) 

      {   maxv=temp_v; 

         保存该B数组; 

字串1

      } 

   } 

}

数据结构经典问题和算法分析(三)递推法

来源:

  作者:

  2007-5-3021:

31:

13  字体:

[大中小]

三、递推法  

   递推法是利用问题本身所具有的一种递推关系求问题解的一种方法。

设要求问题规模为N的解,当N=1时,解或为已知,或能非常方便地得到解。

能采用递推法构造算法的问题有重要的递推性质,即当得到问题规模为i-1的解后,由问题的递推性质,能从已求得的规模为1,2,…,i-1的一系列解,构造出问题规模为I的解。

这样,程序可从i=0或i=1出发,重复地,由已知至i-1规模的解,通过递推,获得规模为i的解,直至得到规模为N的解。

 

【问题】   阶乘计算 

问题描述:

编写程序,对给定的n(n≤100),计算并输出k的阶乘k!

(k=1,2,…,n)的全部有效数字。

 

由于要求的整数可能大大超出一般整数的位数,程序用一维数组存储长整数,存储长整数数组的每个元素只存储长整数的一位数字。

如有m位成整数N用数组a[ ]存储:

 

   N=a[m]×10m-1+a[m-1]×10m-2+ … +a[2]×101+a[1]×100 

并用a[0]存储长整数N的位数m,即a[0]=m。

按上述约定,数组的每个元素存储k的阶乘k!

的一位数字,并从低位到高位依次存于数组的第二个元素、第三个元素……。

例如,5!

=120,在数组中的存储形式为:

  字串1

 3   0   2   1   …… 

首元素3表示长整数是一个3位数,接着是低位到高位依次是0、2、1,表示成整数120。

 

   计算阶乘k!

可采用对已求得的阶乘(k-1)!

连续累加k-1次后求得。

例如,已知4!

=24,计算5!

,可对原来的24累加4次24后得到120。

细节见以下程序。

 

# include  

# include  

# define  MAXN   1000 

void  pnext(int a[ ],int k) 

{   int *b,m=a[0],i,j,r,carry; 

   b=(int * ) malloc(sizeof(int)* (m+1)); 

   for ( i=1;i<=m;i++)      b[i]=a[i]; 

   for ( j=1;j<=k;j++) 

   {   for ( carry=0,i=1;i<=m;i++) 

      {   r=(i

a[i]+b[i]:

a[i])+carry; 

字串7

         a[i]=r%10; 

         carry=r/10; 

      } 

      if (carry)  a[++m]=carry; 

   } 

   free(b); 

   a[0]=m; 

void  write(int *a,int k) 

{   int i; 

   printf(“%4d!

=”,k); 

   for (i=a[0];i>0;i--) 

      printf(“%d”,a[i]); 

printf(“\n\n”); 

void main() 

{   int a[MAXN],n,k; 

   printf(“Enter the number n:

  “); 

   scanf(“%d”,&n); 

字串7

   a[0]=1; 

   a[1]=1; 

   write(a,1); 

   for (k=2;k<=n;k++) 

   {   pnext(a,k); 

      write(a,k); 

      getchar(); 

   } 

}

数据结构经典问题和算法分析

(二)穷举搜索法

来源:

  作者:

  2007-5-3021:

26:

51  字体:

[大中小]

二、穷举搜索法 

     穷举搜索法是对可能是解的众多候选解按某种顺序进行逐一枚举和检验,并从众找出那些符合要求的候选解作为问题的解。

 

【问题】   将A、B、C、D、E、F这六个变量排成如图所示的三角形,这六个变量分别取[1,6]上的整数,且均不相同。

求使三角形三条边上的变量之和相等的全部解。

如图就是一个解。

 

程序引入变量a、b、c、d、e、f,并让它们分别顺序取1至6的证书,在它们互不相同的条件下,测试由它们排成的如图所示的三角形三条边上的变量之和是否相等,如相等即为一种满足要求的排列,把它们输出。

当这些变量取尽所有的组合后,程序就可得到全部可能的解。

细节见下面的程序。

 

【程序1】 

# include  

void main() 

{   int a,b,c,d,e,f; 

   for (a=1;a<=6;a++)    

      for (b=1;b<=6;b++)      { 

         if (b==a)      continue; 

字串9

         for (c=1;c<=6;c++)      { 

            if (c==a)||(c==b)   continue; 

            for (d=1;d<=6;d++)      { 

               if (d==a)||(d==b)||(d==c)    continue; 

for (e=1;e<=6;e++)      { 

   if (e==a)||(e==b)||(e==c)||(e==d)    continue; 

f=21-(a+b+c+d+e); 

if ((a+b+c==c+d+e))&&(a+b+c==e+f+a))   { 

printf(“%6d,a); 

   printf(“%4d%4d”,b,f); 

   printf(“%2d%4d%4d”,c,d,e); 字串6

   scanf(“%*c”); 

                  } 

               } 

            } 

         } 

      } 

按穷举法编写的程序通常不能适应变化的情况。

如问题改成有9个变量排成三角形,每条边有4个变量的情况,程序的循环重数就要相应改变。

 

   对一组数穷尽所有排列,还有更直接的方法。

将一个排列看作一个长整数,则所有排列对应着一组整数。

将这组整数按从小到大的顺序排列排成一个整数,从对应最小的整数开始。

按数列的递增顺序逐一列举每个排列对应的每个整数,这能更有效地完成排列的穷举。

从一个排列找出对应数列的下一个排列可在当前排列的基础上作部分调整来实现。

倘若当前排列为1,2,4,6,5,3,并令其对应的长整数为124653。

要寻找比长整数124653更大的排列,可从该排列的最后一个数字顺序向前逐位考察,当发现排列中的某个数字比它前一个数字大时,如本例中的6比它的前一位数字4大,这说明还有对应更大整数的排列。

但为了顺序从小到大列举出所有的排列,不能立即调整得太大,如本例中将数字6与数字4交换得到的排列126453就不是排列124653的下一个排列。

为了得到排列124653的下一个排列,应从已经考察过的那部分数字中选出比数字大,但又是它们中最小的那一个数字,比如数字5,与数字4交换。

该数字也是从后向前考察过程中第一个比4大的数字。

5与4交换后,得到排列125643。

在前面数字1,2,5固定的情况下,还应选择对应最小整数的那个排列,为此还需将后面那部分数字的排列顺序颠倒,如将数字6,4,3的排列顺序颠倒,得到排列1,2,5,3,4,6,这才是排列1,2,4,6,5,3的下一个排列。

按以上想法编写的程序如下。

  

【程序2】 

# include  

# define SIDE_N   3 

# define LENGTH   3 

# define VARIABLES   6 

int A,B,C,D,E,F; 

int *pt[]={&A,&B,&C,&D,&E,&F}; 

int *side[SIDE_N][LENGTH]={&A,&B,&C,&C,&D,&E,&E,&F,&A}; 

int side_total[SIDE_N]; 

main{} 

{   int i,j,t,equal; 

   for (j=0;j

      *pt[j]=j+1; 

   while

(1) 

   {   for (i=0;i

      {   for (t=j=0;j

            t+=*side[i][j]; 

         side_total[i]=t; 

字串5

      } 

      for (equal=1,i=0;equal&&i

         if (side_total[i]!

=side_total[i+1]   equal=0; 

      if (equal) 

      {   for (i=1;i

            printf(“%4d”,*pt[i]); 

         printf(“\n”); 

         scanf(“%*c”); 

      } 

      for (j=VARIABLES-1;j>0;j--) 

         if (*pt[j]>*pt[j-1])   break; 

      if (j==0)   break; 

字串7

      for (i=VARIABLES-1;i>=j;i--) 

         if (*pt[i]>*pt[i-1])   break; 

      t=*pt[j-1];* pt[j-1] =* pt[i]; *pt[i]=t; 

      for (i=VARIABLES-1;i>j;i--,j++) 

      {   t=*pt[j]; *pt[j] =* pt[i]; *pt[i]=t;   } 

   } 

从上述问题解决的方法中,最重要的因素就是确定某种方法来确定所有的候选解。

下面再用一个示例来加以说明。

 

【问题】   背包问题 

问题描述:

有不同价值、不同重量的物品n件,求从这n件物品中选取一部分物品的选择方案,使选中物品的总重量不超过指定的限制重量,但选中物品的价值之和最大。

 

设n个物品的重量和价值分别存储于数组w[ ]和v[ ]中,限制重量为tw。

考虑一个n元组(x0,x1,…,xn-1),其中xi=0 表示第i个物品没有选取,而xi=1则表示第i个物品被选取。

显然这个n元组等价于一个选择方案。

用枚举法解决背包问题,需要枚举所有的选取方案,而根据上述方法,我们只要枚举所有的n元组,就可以得到问题的解。

 字串1

显然,每个分量取值为0或1的n元组的个数共为2n个。

而每个n元组其实对应了一个长度为n的二进制数,且这些二进制数的取值范围为0~2n-1。

因此,如果把0~2n-1分别转化为相应的二进制数,则可以得到我们所需要的2n个n元组。

 

【算法】 

maxv=0; 

for (i=0;i<2n;i++) 

{   B[0..n-1]=0; 

   把i转化为二进制数,存储于数组B中; 

   temp_w=0; 

   temp_v=0; 

   for (j=0;j

   {   if (B[j]=

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1