C语言迭代法详细讲解.docx

上传人:b****2 文档编号:25924762 上传时间:2023-06-16 格式:DOCX 页数:18 大小:23.33KB
下载 相关 举报
C语言迭代法详细讲解.docx_第1页
第1页 / 共18页
C语言迭代法详细讲解.docx_第2页
第2页 / 共18页
C语言迭代法详细讲解.docx_第3页
第3页 / 共18页
C语言迭代法详细讲解.docx_第4页
第4页 / 共18页
C语言迭代法详细讲解.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

C语言迭代法详细讲解.docx

《C语言迭代法详细讲解.docx》由会员分享,可在线阅读,更多相关《C语言迭代法详细讲解.docx(18页珍藏版)》请在冰豆网上搜索。

C语言迭代法详细讲解.docx

C语言迭代法详细讲解

迭代法

  迭代法也称辗转法,是一种不断用变量的旧值递推新值的过程,跟迭代法相对应的是直接法(或者称为一次解法),即一次性解决问题。

迭代法又分为精确迭代和近似迭代。

“二分法”和“牛顿迭代法”属于近似迭代法。

  迭代算法是用计算机解决问题的一种基本方法。

它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。

  利用迭代算法解决问题,需要做好以下三个方面的工作:

  一、确定迭代变量。

在可以用迭代算法解决的问题中,至少存在一个直接或间接地不断由旧值递推出新值的变量,这个变量就是迭代变量。

  二、建立迭代关系式。

所谓迭代关系式,指如何从变量的前一个值推出其下一个值的公式(或关系)。

迭代关系式的建立是解决迭代问题的关键,通常可以使用递推或倒推的方法来完成。

  三、对迭代过程进行控制。

在什么时候结束迭代过程?

这是编写迭代程序必须考虑的问题。

不能让迭代过程无休止地重复执行下去。

迭代过程的控制通常可分为两种情况:

一种是所需的迭代次数是个确定的值,可以计算出来;另一种是所需的迭代次数无法确定。

对于前一种情况,可以构建一个固定次数的循环来实现对迭代过程的控制;对于后一种情况,需要进一步分析出用来结束迭代过程的条件。

  例1:

一个饲养场引进一只刚出生的新品种兔子,这种兔子从出生的下一个月开始,每月新生一只兔子,新生的兔子也如此繁殖。

如果所有的兔子都不死去,问到第12个月时,该饲养场共有兔子多少只?

  分析:

这是一个典型的递推问题。

我们不妨假设第1个月时兔子的只数为u1,第2个月时兔子的只数为u2,第3个月时兔子的只数为u3,……根据题意,“这种兔子从出生的下一个月开始,每月新生一只兔子”,则有

  u1=1,u2=u1+u1×1=2,u3=u2+u2×1=4,……

  根据这个规律,可以归纳出下面的递推公式:

  un=un-1×2(n≥2)

  对应un和un-1,定义两个迭代变量y和x,可将上面的递推公式转换成如下迭代关系:

  y=x*2

  x=y

  让计算机对这个迭代关系重复执行11次,就可以算出第12个月时的兔子数。

参考程序如下:

  cls

  x=1

  fori=2to12

  y=x*2

  x=y

  nexti

  printy

  end

  例2:

阿米巴用简单分裂的方式繁殖,它每分裂一次要用3分钟。

将若干个阿米巴放在一个盛满营养参液的容器内,45分钟后容器内充满了阿米巴。

已知容器最多可以装阿米巴220,220个。

试问,开始的时候往容器内放了多少个阿米巴?

请编程序算出。

  分析:

根据题意,阿米巴每3分钟分裂一次,那么从开始的时候将阿米巴放入容器里面,到45分钟后充满容器,需要分裂45/3=15次。

而“容器最多可以装阿米巴2^20个”,即阿米巴分裂15次以后得到的个数是2^20。

题目要求我们计算分裂之前的阿米巴数,不妨使用倒推的方法,从第15次分裂之后的2^20个,倒推出第15次分裂之前(即第14次分裂之后)的个数,再进一步倒推出第13次分裂之后、第12次分裂之后、……第1次分裂之前的个数。

  设第1次分裂之前的个数为x0、第1次分裂之后的个数为x1、第2次分裂之后的个数为x2、……第15次分裂之后的个数为x15,则有

  x14=x15/2、x13=x14/2、……xn-1=xn/2(n≥1)

  因为第15次分裂之后的个数x15是已知的,如果定义迭代变量为x,则可以将上面的倒推公式转换成如下的迭代公式:

  x=x/2(x的初值为第15次分裂之后的个数2^20)

  让这个迭代公式重复执行15次,就可以倒推出第1次分裂之前的阿米巴个数。

因为所需的迭代次数是个确定的值,我们可以使用一个固定次数的循环来实现对迭代过程的控制。

参考程序如下:

  cls

  x=2^20

  fori=1to15

  x=x/2

  nexti

  printx

  end

  ps:

java中幂的算法是Math.pow(2,20);返回double,稍微注意一下

  例3:

验证谷角猜想。

日本数学家谷角静夫在研究自然数时发现了一个奇怪现象:

对于任意一个自然数n,若n为偶数,则将其除以2;若n为奇数,则将其乘以3,然后再加1。

如此经过有限次运算后,总可以得到自然数1。

人们把谷角静夫的这一发现叫做“谷角猜想”。

  要求:

编写一个程序,由键盘输入一个自然数n,把n经过有限次运算后,最终变成自然数1的全过程打印出来。

  分析:

定义迭代变量为n,按照谷角猜想的内容,可以得到两种情况下的迭代关系式:

当n为偶数时,n=n/2;当n为奇数时,n=n*3+1。

用QBASIC语言把它描述出来就是:

  ifn为偶数then

  n=n/2

  else

  n=n*3+1

  endif

  这就是需要计算机重复执行的迭代过程。

这个迭代过程需要重复执行多少次,才能使迭代变量n最终变成自然数1,这是我们无法计算出来的。

因此,还需进一步确定用来结束迭代过程的条件。

仔细分析题目要求,不难看出,对任意给定的一个自然数n,只要经过有限次运算后,能够得到自然数1,就已经完成了验证工作。

因此,用来结束迭代过程的条件可以定义为:

n=1。

参考程序如下:

  cls

  input"Pleaseinputn=";n

  dountiln=1

  ifnmod2=0then

  rem如果n为偶数,则调用迭代公式n=n/2

  n=n/2

  print"—";n;

  else

  n=n*3+1

  print"—";n;

  endif

  loop

  end

  迭代法开平方:

  #include

  #include

  voidmain()

  {

  doublea,x0,x1;

  printf("Inputa:

\n");

  scanf("%lf",&a);//为什么在VC6.0中不能写成“scanf("%f",&a);”?

  if(a<0)

  printf("Error!

\n");

  else

  {

  x0=a/2;

  x1=(x0+a/x0)/2;

  do

  {

  x0=x1;

  x1=(x0+a/x0)/2;

  }while(fabs(x0-x1)>=1e-6);

  }

  printf("Result:

\n");

  printf("sqrt(%g)=%g\n",a,x1);

  }

  求平方根的迭代公式:

x1=1/2*(x0+a/x0)。

  算法:

1.先自定一个初值x0,作为a的平方根值,在我们的程序中取a/2作为a的初值;利用迭代公式求出一个x1。

此值与真正的a的平方根值相比,误差很大。

  2.把新求得的x1代入x0中,准备用此新的x0再去求出一个新的x1.

  3.利用迭代公式再求出一个新的x1的值,也就是用新的x0又求出一个新的平方根值x1,此值将更趋近于真正的平方根值。

  4.比较前后两次求得的平方根值x0和x1,如果它们的差值小于我们指定的值,即达到我们要求的精度,则认为x1就是a的平方根值,去执行步骤5;否则执行步骤2,即循环进行迭代。

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

设方程为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);

  }

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

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

  设方程组为:

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

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

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

  {for(i=0;i

  x=初始近似根;

  do{

  for(i=0;i

  y=x;

  for(i=0;i

  x=gi(X);

  for(delta=0.0,i=0;i

  if(fabs(y-x)>delta)delta=fabs(y-x);

  }while(delta>Epsilon);

  for(i=0;i

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

  printf(“\n”);

  }

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

  

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

  

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

  递归

  递归是设计和描述算法的一种有力的工具,由于它在复杂算法的描述中被经常采用,为此在进一步介绍其他算法设计方法之前先讨论它。

  能采用递归描述的算法通常有这样的特征:

为求解规模为N的问题,设法将它分解成规模较小的问题,然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法,分解成规模更小的问题,并从这些更小问题的解构造出规模较大问题的解。

特别地,当规模N=1时,能直接得解。

  【问题】编写计算斐波那契(Fibonacci)数列的第n项函数fib(n)。

  斐波那契数列为:

0、1、1、2、3、……,即:

  fib(0)=0;

  fib

(1)=1;

  fib(n)=fib(n-1)+fib(n-2)(当n>1时)。

  写成递归函数有:

  intfib(intn)

  {if(n==0)return0;

  if(n==1)return1;

  if(n>1)returnfib(n-1)+fib(n-2);

  }

  递归算法的执行过程分递推和回归两个阶段。

在递推阶段,把较复杂的问题(规模为n)的求解推到比原问题简单一些的问题(规模小于n)的求解。

例如上例中,求解fib(n),把它推到求解fib(n-1)和fib(n-2)。

也就是说,为计算fib(n),必须先计算fib(n-1)和fib(n-2),而计算fib(n-1)和fib(n-2),又必须先计算fib(n-3)和fib(n-4)。

依次类推,直至计算fib

(1)和fib(0),分别能立即得到结果1和0。

在递推阶段,必须要有终止递归的情况。

例如在函数fib中,当n为1和0的情况。

  在回归阶段,当获得最简单情况的解后,逐级返回,依次得到稍复杂问题的解,例如得到fib

(1)和fib(0)后,返回得到fib

(2)的结果,……,在得到了fib(n-1)和fib(n-2)的结果后,返回得到fib(n)的结果。

  在编写递归函数时要注意,函数中的局部变量和参数知识局限于当前调用层,当递推进入“简单问题”层时,原来层次上的参数和局部变量便被隐蔽起来。

在一系列“简单问题”层,它们各有自己的参数和局部变量。

  由于递归引起一系列的函数调用,并且可能会有一系列的重复计算,递归算法的执行效率相对较低。

当某个递归算法能较方便地转换成递推算法时,通常按递推算法编写程序。

例如上例计算斐波那契数列的第n项的函数fib(n)应采用递推算法,即从斐波那契数列的前两项出发,逐次由前两项计算出下一项,直至计算出要求的第n项。

  【问题】组合问题

  问题描述:

找出从自然数1、2、……、n中任取r个数的所有组合。

例如n=5,r=3的所有组合为:

(1)5、4、3

(2)5、4、2(3)5、4、1

  (4)5、3、2(5)5、3、1(6)5、2、1

  (7)4、3、2(8)4、3、1(9)4、2、1

  (10)3、2、1

  分析所列的10个组合,可以采用这样的递归思想来考虑求组合函数的算法。

设函数为voidcomb(intm,intk)为找出从自然数1、2、……、m中任取k个数的所有组合。

当组合的第一个数字选定时,其后的数字是从余下的m-1个数中取k-1数的组合。

这就将求m个数中取k个数的组合问题转化成求m-1个数中取k-1个数的组合问题。

设函数引入工作数组a[]存放求出的组合的数字,约定函数将确定的k个数字组合的第一个数字放在a[k]中,当一个组合求出后,才将a[]中的一个组合输出。

第一个数可以是m、m-1、……、k,函数将确定组合的第一个数字放入数组后,有两种可能的选择,因还未去顶组合的其余元素,继续递归去确定;或因已确定了组合的全部元素,输出这个组合。

细节见以下程序中的函数comb。

  【程序】

  #include

  #defineMAXN100

  inta[MAXN];

  voidcomb(intm,intk)

  {inti,j;

  for(i=m;i>=k;i--)

  {a[k]=i;

  if(k>1)

  comb(i-1,k-1);

  else

  {for(j=a[0];j>0;j--)

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

  printf(“\n”);

  }

  }

  }

  voidmain()

  {a[0]=3;

  comb(5,3);

  }

  【问题】背包问题

  问题描述:

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

  设n件物品的重量分别为w0、w1、…、wn-1,物品的价值分别为v0、v1、…、vn-1。

采用递归寻找物品的选择方案。

设前面已有了多种选择的方案,并保留了其中总价值最大的方案于数组option[],该方案的总价值存于变量maxv。

当前正在考察新方案,其物品选择情况保存于数组cop[]。

假定当前方案已考虑了前i-1件物品,现在要考虑第i件物品;当前方案已包含的物品的重量之和为tw;至此,若其余物品都选择是可能的话,本方案能达到的总价值的期望值为tv。

算法引入tv是当一旦当前方案的总价值的期望值也小于前面方案的总价值maxv时,继续考察当前方案变成无意义的工作,应终止当前方案,立即去考察下一个方案。

因为当方案的总价值不比maxv大时,该方案不会被再考察,这同时保证函数后找到的方案一定会比前面的方案更好。

  对于第i件物品的选择考虑有两种可能:

  

(1)考虑物品i被选择,这种可能性仅当包含它不会超过方案总重量限制时才是可行的。

选中后,继续递归去考虑其余物品的选择。

  

(2)考虑物品i不被选择,这种可能性仅当不包含物品i也有可能会找到价值更大的方案的情况。

  按以上思想写出递归算法如下:

  try(物品i,当前选择已达到的重量和,本方案可能达到的总价值tv)

  {/*考虑物品i包含在当前方案中的可能性*/

  if(包含物品i是可以接受的)

  {将物品i包含在当前方案中;

  if(i

  try(i+1,tw+物品i的重量,tv);

  else

  /*又一个完整方案,因为它比前面的方案好,以它作为最佳方案*/

  以当前方案作为临时最佳方案保存;

  恢复物品i不包含状态;

  }

  /*考虑物品i不包含在当前方案中的可能性*/

  if(不包含物品i仅是可男考虑的)

  if(i

  try(i+1,tw,tv-物品i的价值);

  else

  /*又一个完整方案,因它比前面的方案好,以它作为最佳方案*/

  以当前方案作为临时最佳方案保存;

  }

  为了理解上述算法,特举以下实例。

设有4件物品,它们的重量和价值见表:

  物品0123

  重量5321

  价值4431

  并设限制重量为7。

则按以上算法,下图表示找解过程。

由图知,一旦找到一个解,算法就进一步找更好的佳。

如能判定某个查找分支不会找到更好的解,算法不会在该分支继续查找,而是立即终止该分支,并去考察下一个分支。

  按上述算法编写函数和程序如下:

  【程序】

  #include

  #defineN100

  doublelimitW,totV,maxV;

  intoption[N],cop[N];

  struct{doubleweight;

  doublevalue;

  }a[N];

  intn;

  voidfind(inti,doubletw,doubletv)

  {intk;

  /*考虑物品i包含在当前方案中的可能性*/

  if(tw+a.weight<=limitW)

  {cop=1;

  if(i

  else

  {for(k=0;k

  option[k]=cop[k];

  maxv=tv;

  }

  cop=0;

  }

  /*考虑物品i不包含在当前方案中的可能性*/

  if(tv-a.value>maxV)

  if(i

  else

  {for(k=0;k

  option[k]=cop[k];

  maxv=tv-a.value;

  }

  }

  voidmain()

  {intk;

  doublew,v;

  printf(“输入物品种数\n”);

  scanf((“%d”,&n);

  printf(“输入各物品的重量和价值\n”);

  for(totv=0.0,k=0;k

  {scanf(“%1f%1f”,&w,&v);

  a[k].weight=w;

  a[k].value=v;

  totV+=V;

  }

  printf(“输入限制重量\n”);

  scanf(“%1f”,&limitV);

  maxv=0.0;

  for(k=0;kfind(0,0.0,totV);

  for(k=0;k

  if(option[k])printf(“%4d”,k+1);

  printf(“\n总价值为%.2f\n”,maxv);

  }

  作为对比,下面以同样的解题思想,考虑非递归的程序解。

为了提高找解速度,程序不是简单地逐一生成所有候选解,而是从每个物品对候选解的影响来形成值得进一步考虑的候选解,一个候选解是通过依次考察每个物品形成的。

对物品i的考察有这样几种情况:

当该物品被包含在候选解中依旧满足解的总重量的限制,该物品被包含在候选解中是应该继续考虑的;反之,该物品不应该包括在当前正在形成的候选解中。

同样地,仅当物品不被包括在候选解中,还是有可能找到比目前临时最佳解更好的候选解时,才去考虑该物品不被包括在候选解中;反之,该物品不包括在当前候选解中的方案也不应继续考虑。

对于任一值得继续考虑的方案,程序就去进一步考虑下一个物品。

  【程序】

  #include

  #defineN100

  doublelimitW;

  intcop[N];

  structele{doubleweight;

  doublevalue;

  }a[N];

  intk,n;

  struct{int;

  doubletw;

  doubletv;

  }twv[N];

  voidnext(inti,doubletw,doubletv)

  {twv.=1;

  twv.tw=tw;

  twv.tv=tv;

  }

  doublefind(structele*a,intn)

  {inti,k,f;

  doublemaxv,tw,tv,totv;

  maxv=0;

  for(totv=0.0,k=0;k

  totv+=a[k].value;

  next(0,0.0,totv);

  i=0;

  While(i>=0)

  {f=twv.;

  tw=twv.tw;

  tv=twv.tv;

  switch(f)

  {case1:

twv.++;

  if(tw+a.weight<=limitW)

  if(i

  {next(i+1,tw+a.weight,tv);

  i++;

  }

  else

  {maxv=tv;

  for(k=0;k

  cop[k]=twv[k].!

=0;

  }

  break;

  case0:

i--;

  break;

  default:

twv.=0;

  if(tv-a.value>maxv)

  if(i

  {next(i+1,tw,tv-a.value);

  i++;

  }

  else

  {maxv=tv-a.value;

  for(k=0;k

  cop[k]=twv[k].!

=0;

  }

  break;

  }

  }

  returnmaxv;

  }

  voidmain()

  {doublemaxv;

  printf(“输入物品种数\n”);

  scanf((“%d”,&n);

  printf(“输入限制重量\n”);

  scanf(“%1f”,&limitW);

  printf(“输入各物品的重量和价值\n”);

  for(k=0;k

  scanf(“%1f%1f”,&a[k].weight,&a[k].v

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

当前位置:首页 > 工程科技 > 环境科学食品科学

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

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