版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第5章循环结构5.1while循环5.2do-while循环5.3for循环5.4循环嵌套5.5转移控制语句5.6程序举例习题5
本章学习要求:
1.了解goto语句的使用。
2.掌握for语句、while语句和do-while语句的使用以及它们之间的区别。
3.掌握break、continue语句与循环语句的结合使用;循环语句的嵌套使用。
4.掌握循环语句解决算法问题之数列问题、穷举算法、密码问题。
5.掌握二分法、牛顿迭代法求非线性方程的根,了解梯形法求定积分。
循环结构用来描述具有规律性的重复工作。例如,求若干个数的累积;输入全班所有学生的成绩等。实际上,几乎所有实用的程序都包括循环。循环结构是结构化程序设计的基本结构之一,与顺序结构、选择结构一起作为各种复杂程序的基本构造单元。循环结构的应用使得大量重复的工作变得更容易,提高了编程效率。C语言提供了三种循环结构的语句:while、do-while和for语句。
在第1章中,我们讲述了循环结构有当型循环结构与直到型循环结构,前者是先进行条件判断,再决定是否执行循环体;后者是先执行一次循环体,再进行条件判断。
while循环是当型循环。
1. while循环的一般格式
while循环的一般格式如下:
while(表达式)
循环体;5.1while循环说明:
(1)“表达式”可以是任意表达式,用来判定循环是否终止(或者说判定循环体是否执行)。表达式的值要么为真,要么为假;如果其值为一个数值,则非0的任何数都为真,而0代表假。如果第一次执行时,“表达式”的值为假,则“循环体”中的语句一次都不执行。
(2)“循环体”中可以是单语句,也可以是空语句(只有一个;的语句)或复合语句,如果是复合语句,则必须括在{}之中。
2. while循环语句的执行顺序
while循环语句的执行顺序如下:
(1)先计算表达式的值,再判断表达式的值是真还是假;
(2)如果是真,则执行循环体,再转到第(1)步继续;
(3)否则退出循环,转而执行本语句后的其他语句。
图5.1是用流程图来表示while语句的执行顺序,虚线框内是while语句部分。
图5.1while循环语句流程图
3. while语句使用举例
例5.1
计算s
=
1
+
2
+
3
+
…+
100。
问题分析:解决这类累加、累积问题,关键是要找出通用的计算公式,即找到循环体中的部分。s
=
s
+
i是本例的通用性计算公式。公式中,s既作为一个加数使用,也用于存放累加后的结果,其初值为0;i既作为另一个加数使用,也作为循环变量,其取值范围是1~100。对于每个i值,要进行一次s
=
s
+
i的计算。例如,当i
=
1时,s
=
0
+
1
=
1;当i
=
2时,s
=
1
+
2
=
3;当i
=
3时,s
=
2
+
3
=
5;……依此类推下去,就可以得到结果s(本例的流程图见图5.2)。
图5.2累加流程图程序如下:
main()
{
inti,s;
s=0; /*存放累加结果的变量赋初值*/
i=1; /*从1开始累加*/
while(i<=100) /*i小于等于100时继续执行循环体*/
{s=s+i; /*累加*/
i++;
}
printf("s=%d\n",s); /*输出累加后的结果*/
}
do-while循环是直到型循环结构,其流程图如图1.4(b)所示。
直到型循环的执行过程是,首先执行循环体,然后判断条件(计算逻辑表达式),如果条件满足(即逻辑表达式值为真),则继续执行循环体;如果条件不满足(即逻辑表达式值为假),则退出循环结构。5.2do-while循环从上述的执行过程来看,直到型循环结构由于首先执行循环体,然后再判断条件,因此,其循环体至少要执行一次。这是与当型循环结构最明显的区别。
1. do-while循环的一般格式
do-while循环的一般格式如下:
do
循环体
while(表达式);说明:
(1)“表达式”可以是任意表达式,用来判定循环是否终止(或者说判定循环体是否执行)。表达式的值要么为真,要么为假;如果其值为一个数值,则非0的任何数都为真,而0代表假。即使第一次执行时“表达式”的值为假,“循环体”中的语句最少会被执行一次。
(2)“循环体”中可以是单语句,也可以是空语句(只有一个;的语句)或复合语句,如果是复合语句,则必须括在{}之中。
(3)
do-while作为一个语句,必须以分号(;)结束,所以在while(表达式)后一定要有分号。
2. do-while循环语句的执行顺序
do-while循环语句的执行顺序如下:
(1)先执行循环体;
(2)计算表达式的值,并判断表达式的值是真还是假;
(3)如果是真,则继续转到第(1)步执行循环体;
(4)否则退出循环,转而执行本语句后的其他语句。
图5.3是用流程图来表示do-while语句的执行顺序,虚线框内是do-while语句部分。
图
5.3do-while循环语句流程图
3. do-while语句使用举例
例5.1中的问题也可以用do-while循环语句来实现,其C程序如下:
main()
{inti,s;
s=0;
i=1;
do
{s=s+i; /*累加*/
i++;
}while(i<=100); /*i的值小于等于100时继续循环*/
printf("s=%d\n",s); /*输出累加结果*/
}
例5.2
编写程序计算并输出下列级数和,直到某项的绝对值小于10-4为止。
问题分析:
(1)这是一个逐项相加的问题,第一项可以看成sum的初值;
(2)每一项的正或负交替进行(即各项符号相间),因此可以设一个改变每一项符号的变量f,每次累加前将变量f自身取负(即f
=
-f),并将其与该项的和相乘;
(3)第k项的表达式为1/(k*(k
+
1)),可以用一个变量d来存放第k项的值,以便于累加和判断该项的值是否满足条件。
程序如下:
#include<stdio.h>
#include<math.h>
main()
{intk;
doublesum,d,f;
sum=1.0;k=0;f=1.0;
do
{k=k+1;f=-f; /*符号变量f自身取负*/
d=1.0/(k*(k+1)); /*计算第k项的值,试想: 为什么1必须用1.0?*/
sum=sum+f*d; /*累加第k项*/
}while(fabs(d)>=1.0e-4); /*某项的值d的绝对值如果 大于10-4则退出循环*/
printf("sum=%lf\n",sum);
}
对于这一类求级数和的问题,最为关键的是要先找到通项表达式,在找通项表达式时,可以对前后两项进行对比来发现,而本例的通项已知,所以不必再去找通项。
例5.3
从键盘输入1个整数,把这个整数中的各位数字反序输出。例如,输入12345,则输出为54321。
问题分析:所谓反序输出就是先输出个位数,再输出十位数……。对任意整数k而言,可以用k%10求得个位数,例如,12345$10
=
5。在此基础上,将k的值用k/10来代替,即用k/10将k缩小10倍。例如,12345/10
=
1234(试想为什么不等于1234.5?)。这样,k就由原来的5位数变成了4位数。用同样的方法可以求得该4位数的个位数(即原5位数的十位数),再将4位数缩小为3位数,依此类推,直到剩1位数。在1位数的情况下,直接输出该数,整个问题就解决了。剩下的问题就是如何判断k已经缩小为1位数,如果k为1位数,则必有k/10=0。因此,解决这个问题的基本思路是不断求出k的个位数,将该个位数输出,然后将k缩小10倍。如果缩小后的k不等于0,就继续进入下一轮循环;否则,退出循环。程序如下:
main()
{
intk,g;
printf("Pleaseinputaninteger:");
scanf("%d",&k);
do
{
g=k%10;
printf("%2d",g);
k=k/10;
}while(k!=0);
}
前面介绍的while、do-while循环语句对于循环体执行的次数事先无法估计的情况,是十分有效的。但在有些实际问题中,循环体的执行次数是可以事先估计出来(或已知)的,在这种情况,虽然也可以用前面介绍的两种循环语句来实现,但在C语言中还提供了另一种实现循环的语句,即for语句。5.3for循环
1. for循环语句的一般格式
for循环语句的一般格式如下:
for(表达式1;表达式2;表达式3)
循环体语句(组);
一般来说,表达式1用于提供循环的初始值(经常用逗号运算符给多个变量赋初值),表达式2用于提供循环的条件,表达式3用于改变循环变量的值,表达式3的位置相当于在循环体中的最后一个语句。
for循环语句在功能上等价于下列的while循环语句:
表达式1;
while(表达式2)
{循环体语句(组);
表达式3;
}
2. for循环语句的执行顺序
for循环语句的执行顺序如下:
(1)执行表达式1;
(2)计算表达式2的值;
(3)如果表达式2的值为真,则执行循环体,再执行表达式3,转到第(2)步执行;
(4)如果表达式2的值为假,则结束循环的执行,转而去执行for语句后的语句。
可以用图5.4来表示for语句的执行过程。
图5.4for语句的执行过程下面是用for循环语句求解例5.1中问题的程序:
main()
{inti,s;
s=0;
for(i=1;i<=100;i++)/*i的值小于等于100时继续循环*/
{s=s+i; /*累加*/
}
printf("s=%d\n",s); /*输出累加结果*/
}
例5.4
在0°~360°之间,每隔20°计算并输出正弦与余弦值。
程序如下:
#include<stdio.h>
#include<math.h>
#definePI3.1415926
main()
{
intk;
doublef;
for(k=0;k<=360;k=k+20)
{f=PI*k/180;
printf("sin(f)=%lf,cos(f)=%lf\n",sin(f),cos(f));
}
}
下面对for循环语句作几点说明:
(1)在for语句中,三个表达式中的任何一个表达式都可省略,但其中的两个“;”不能省略。例如,下面的四种循环语句是等价形式:
①
for(i=1;i<=100;i++)循环体
②
i=1;
for(;i<=100;i++)循环体
在这种形式中,for语句将提供循环初值的表达式1放到for语句前,从而省略了表达式1。
③
i=1;
for(;i<=100;){循环体;i++;}
在这种形式中,for语句将提供循环初值的表达式1放到for语句前,从而省略了表达式1;并且将表达式3放到循环体中作为循环体的最后一个语句。
④
i=1;
while(i<=100){循环体;i++;}
在这种形式中,是将第③种形式的for语句换成while语句,这也是将for循环语句改成while语句的方法,即将表达式1放到循环前面,将表达式3放到循环体中作为最后一个语句,最后将for语句换成while。
(2)在for循环语句中,循环体中可以是单语句,也可以是空语句(只有一个
;的语句)或复合语句,如果是复合语句,则必须括在{}之中。
(3)下面两种形式可能导致死循环:
for(表达式1;;表达式3)循环体
与
for(;;)循环体上面两个语句都没有用于判断循环结束的条件(即表达式2)。在C语言中,一个空表达式的值一般为非零值。因此,在for语句中如果省略了表达式2,就意味着表达式2的值一直为真,从而可能导致死循环。在这种情况下,要想避免死循环,可以在循环体中增加if语句+转移控制语句跳出循环(还可以使用if+goto形式退出循环),比如可以使用
if(表达式)break;
(4)
for循环也是当型循环结构,只不过它对于事先可以确定循环次数的问题特别方便,如我们在解决例5.1中的问题时。
在C语言中,虽然for循环语句的形式很灵活,但从程序的可读性考虑,建议使用如下两种形式:
for(i=初值;i<=终值;i=i+步长)循环体
与
for(i=初值;i>=终值;i=i-步长)循环体
所谓循环嵌套,是指一个循环体中完整地包含了另一个循环结构,又称多重循环。在C语言中,允许循环结构多重嵌套,并且while、do-while和for循环语句都可以嵌套。另外,在一个循环体内还可以包括各种完整的选择结构,在一个选择结构的某个独立部分中,还可以包括完整的循环结构。5.4循环嵌套
例5.5
打印九九乘法表。
问题分析:九九乘法表中,都是由一个1位整数(用变量i来表示)乘以另一个1位整数(用变量j来表示)。i和j的取值范围都是1~9,当i
=
1时,j可以取1~9的值,得到:
1*1
=
11*2
=
2…
当i=2时,j同样可以取1~9的值,得到:
2*1
=
22*2
=
4…
…
当i=9时,j可以取1~9的值,得到:
9*1
=
99*2
=
18…
因此,如果用i来作为外循环的变量,而用j作为内循环的变量,可得程序如下:
main()
{inti,j;
for(i=1;i<=9;i++)
{for(j=1;j<=9;j++)
printf("%d*%d=%2d",i,j,i*j);
printf("\n");
}
}程序输出结果如下:
1*1=1 1*2=2 1*3=3 1*4=4 1*5=5 1*6=6 1*7=7
2*1=2 2*2=4 2*3=6 2*4=8 2*5=10 2*6=12 2*7=14 1*8=8
1*9=9
2*9=18
2*8=16
…
9*1=9 9*2=189*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
例5.6
顺序输出3~100之间的所有素数。
问题分析:素数就是只能被1和自身整除的数,判断一个正整数n(n>2)是否是素数,可以有以下两种方法:
(1)根据素数的定义来判断一个正整数n是否是素数,因为素数只能被1和自身整除,所以将n与2~n-1逐个判断能否整除,并且用变量k进行计数(能整除的数的个数),再判断k是否为0,如果为0则n是素数,否则n就不是素数。判断n是否是素数程序段如下:
k=0; /*计数变量k清零*/
for(j=2;j<=n-1;j++)
if(n%j==0){k++;break;}/*如果n能被j整除,则k加1*/
if(k==0)printf("%d",n); /*如果n是素数,就输出n*/
由于要输出的是3~100之间的所有素数,则n的取值范围应是3~100,因此全部程序如下:
#include<stdio.h>
main()
{intj,k,n,m=0;
for(n=3;n<=100;n++)
{
k=0; /*计数变量k清零*/
for(j=2;j<=n-1;j++)
if(n%j==0){k++;break;} /*如果n能被j整 除,则k加1*/
if(k==0){m++;printf(“%5d”,n);} /*如果n是素数,就 输出n*/
if(m%10==0)printf(“\n”); /*每行输出10个素 数*/
}
}
(2)上述方法是根据素数的定义来进行判断的,循环的次数比较多。因此,我们可以采用下面的方法:用2到之间的整数j去除n,如果所有的j均不能整除n,则n为素数,否则n不是素数,这种方法的流程图可以参见第1章中的图1.2。
由于n的取值范围是3~100,因此外循环的循环变量使用n,如:
for(n=3;n<=100;n++)
{
判断n是否是素数
}
在内循环中判断n是否是素数的程序如下:
k=0; /*计数变量k清零*/
for(j=2;j<=sqrt(n);j++)
if(n%j==0){k++;break;}
if(k==0){m++;printf(“%5d”,n);} /*如果n是素数,就 输出n*/
完整的程序如下:
#include<stdio.h>
#include<math.h>
main()
{intj,k,n,m=0;
for(n=3;n<=100;n++)
{
k=0; /*计数变量k清零*/
for(j=2;j<=sqrt(n);j++)
if(n%j==0){k++;break;} /*如果n能被j整 除,则k加1*/
if(k==0){m++;printf(“%5d”,n);} /*如果n是素数,就 输出n*/
if(m%10==0)printf(“\n”);
/*每行输出10个素数*/
}
}
5.5.1break语句
在第4章介绍多分支选择结构switch时,曾经提到过break语句,它的功能是退出switch语句。实际上,C语言中break语句具有以下两个功能:
(1)跳出switch结构;
(2)退出当前循环结构,包括while语句、do-while语句和for语句。5.5转移控制语句
例5.7break在循环结构中的控制作用示例。
main()
{intk;
for(k=1;k<=100;k++)
{printf("%d",k);
if(k==20)break;
}
}
在程序中如果没有break语句,for循环将执行100次。由于break语句的存在,使得当k=20时,break就被执行,循环被提前结束,从而程序运行结束,因此,程序只输出1~20这20个整数,而不是输出100个整数。说明:
(1)
break通常要和if语句联用,才能提前结束循环。在嵌套的循环中,break只能退出它所在的那一层循环。
(2)当循环体中包含switch语句,而break位于switch语句中时,break只强迫程序流程退出该switch而不是退出switch所在的循环。例如:
for(i=1;i<=10;i++)
{switch(i)
{case1:printf("%d",1);break;
case2:printf("%d",2);break;
default:printf("大于2");break;
}
}
在switch语句中的break只是退出switch语句,而对于循环结构并无影响。5.5.2continue语句
continue语句的功能是结束本次循环的执行,转而去执行下一次循环(即去计算条件表达式),但不退出循环结构。
continue语句在C语言中只能用在循环结构中,包括while语句、do-while语句和for语句。
例5.8continue在循环结构中的控制作用示例。
main()
{intk;
for(k=1;k<=100;k++)
{if(k%5==0)continue;
printf("%d",k);
}
}上面的程序如果没有continue,则会输出1~100共100个整数,由于用到continue,使得程序流程每当k能被5整除时就不输出,因此,最后输出的是1~100之间不能被5整除的整数。注意:在for循环语句中,当执行到continue语句时,不是直接去执行“表达式2”,而是先执行“表达式3”,再去执行“表达式2”,即continue语句的执行只是在本次循环中不再执行循环体中continue以后的语句,但“表达式3”还是要执行的。说明:
(1)
continue语句只能用在循环结构中;
(2)
continue语句通常要和if语句联用,它只能提前结束本次循环,立即进入下一次循环条件的判断(如果是for语句,则先执行“表达式3”),并不能终止整个循环。
(3)如果循环体中包含switch语句,而且continue位于switch结构中,该continue也只对循环起作用,下面的程序可以说明这一点。
main()
{intk=0;
charc='A';
do
{switch(c++)
{case'A':k++;break;
case'B':k--;
case'C':k+=2;break;
case'D':k=k%2;
continue;
case'E':k=k*10;
break;
default:k=k/3;
}
k++;
}while(c<'G');
printf("%d\n",k);
}
上面程序中的break位于switch结构中,因此它只对switch语句起作用,而对循环不产生影响。continue虽然也位于switch结构中,但它只对do-while循环语句产生影响,而不影响switch语句。阅读本程序时,需要注意以下两点:
(1)每次计算switch中的表达式c++时,总是先取c的当前值,然后再将c的值加1。例如,第一次执行switch时,c的当前值为“A”,c加1后的值为“B”。将当前值(“A”)与各个case中的常量进行匹配。
(2)一旦执行到含有continue的case时,就会提前进入下一轮循环,即跳过switch语句中后面的语句,同时跳过循环中switch语句后的语句,而直接转到条件判断表达式(即计算while(c<'G'))。
表5.1给出了本例程序在运行过程中变量c和k值的变化情况。
表5.1变量c和k值的变化情况5.5.3goto语句
goto语句不是结构化程序设计的语句,因此,多年来都建议编程者尽可能少用goto语句。另外在C语言中,因为有许多的控制结构,并且可以使用break和continue这样的转移控制语句,所以goto语句也可以不使用。
在多重循环中,如果需要从最内层循环一次退出,则使用goto语句的效果非常明显。
1. goto语句的一般格式
goto语句的一般格式如下:
goto语句标号;
语句标号用标识符表示,它的命名规则与变量名相同,即以字母或下划线为首后跟字母、数字或下划线。不能用整数来作标号,但标号可以与变量同名。例如:
gotolable_1;
是合法的。
goto语句的主要功能:控制程序流程转移到指定标号处执行。由于标号可以重置,因此使用goto语句可以选择不唯一的跳转目的地。
2. goto语句的用途
一般来说,goto语句有以下两种用途:
(1)与if语句一起构成循环结构;
(2)从循环体中跳转到循环体外。如果只是从一重循环中跳出,可以使用break语句,而如果从多重循环的内层循环中跳出来,就需要用到goto语句。但这种用法不符合结构化编程的原则,一般很少采用,只有在需要大大提高效率时才使用。如果用if语句和goto语句来解决例5.1的问题,则程序如下:
main()
{inti,s;
s=0;
i=1;
loop:if(i<=100) /*i的值小于等于100时执行循环,在语句前加一个标号loop*/
{s=s+i; /*累加*/
i++;
gotoloop; /*使用goto语句转移到标号loop处*/
}
printf("s=%d\n",s);/*输出累加结果*/
}
例5.9
从6~80中找出第一个素数。
问题分析:本例题要求只要找到6~80中的第一个素数,后面的数就不用找了。
程序如下:
#include<stdio.h>
#include<math.h>
main()
{intj,k,n;
for(n=6;n<=80;n++)
{
k=0; /*计数变量k清零*/
for(j=2;j<=sqrt(n);j++)
if(n%j==0){k++;break;} /*如果n能被j整除,则k加 1*/
if(k==0)gotolable_1; /*如果n是素数,就退出循 环*/
}
lable_1:if(k==0)printf(“第一个素数是:%d”,n);
/*输出 找到的第一个素数*/
}
5.6.1数列问题(累加、累积、递推)
例5.10
求s=1+2+…+n。
问题分析:本题是一个累加问题,其中数n需要输入(必须大于等于1),而累加的结果需要放在变量s中。程序流程图如图5.5所示。5.6程序举例图5.5例5.10程序流程图程序如下:
main()
{
inti,n,s;
s=0; /*给s赋初值,注意:存放和的变 量s的初始值应为0*/
printf("Pleaseinputn:");
scanf("%d",&n); /*输入变量n的值*/
for(i=1;i<=n;i++) /*计算累加和s*/
s+=i;
printf("\nSum=%d\n",s); /*输出和*/
}
程序运行结果:
Pleaseinputn:100
Sum=5050
如果将本例改成求s=1*2*…*n,则只需将程序中的两处进行修改:
(1)将s=0改成s=1;
(2)将循环体中的语句s+=i;改成s*=i;。
例5.11
计算e的近似值。
要求使用泰勒级数展开式来求自然对数的底e的近似值,泰勒级数展开式如下:
(设n=10)
问题分析:这是一个既要求累乘、又要求累加的问题。用e来表示级数前n项的和,用n来表示第i项的分母,用i对项数进行计数,用e
=
e
+
1/n来累加各项。
程序如下:
main()
{
inti=1;
floate=1,n=1;
while(i<=10) /*i大于10时退出循环*/
{
n=n*i; /*计算第i项的分母*/
e=e+1/n; /*将第i项累加到e中去*/
i++;
}
printf("e=%f\n",e);
}
程序运行结果:
e=2.718282
例5.12
以下程序采用递推法计算
的值,即求 的值,其中x为键盘输入的一个任意的单精度实数。请将程序完善。
#include<stdio.h>
main()
{inti,sign=1;
floatx,s,t;
scanf("%f",&x);
s=[1]; /*给存放和的变量s赋初值*/
t=x;
for(i=1;i<=10;i++)
{sign=-sign;
t=t*[2] ; /*计算第i项*/
s+=t*sign; /*累加求和*/
}
printf("s=%6.2f",s);
}问题分析:从程序中可以看出,变量sign用来交替变换奇偶数项的正负号,变量s用来存放累加和,变量t用来存放第i项的值。所以第1个空应该是给和s赋初值,由于for循环中的i是从1开始的(即从第二项开始累加),因此第1个空应填x;[2]空这一行程序显然是计算第i项的值,第i项的值应该是前一项乘上符号变化变量sign和增项,则第2个空中应填增项,所谓增项就是第i-1项与第i项不同的地方,应该是x*x/i。
答案应为:[1]
x;[2]
x*x/i。5.6.2穷举法
穷举法就是在一个集合内对每个元素一一测试,直到找出满足条件的一组值为止。穷举法对人们来说常常是单调而又繁琐的工作,但对计算机来说,重复计算正好可以用简洁的程序发挥它运算速度快的优势。比如,在网络中黑客经常使用一个密码样本库对需要密码认证部分进行穷举式测试,即黑客不断地从密码样本库中取出一个密码去试能否过关,如果不行,再取一个密码去试,一直进行下去,直到找到一个能过关的密码或密码样本库中的密码都测试完为止。
下面通过例子让大家了解穷举法。
例5.13
搬砖问题:36块砖,36人搬,男搬4块砖、女搬3块砖、两个小孩合搬一块砖,要求一次全搬完,问男、女、小孩各需多少?
问题分析:设男、女、小孩各为men、women、children,则可得到如下方程式:
4*men+3*women+children/2=36
men+women+children=36
这个方程组有三个变量,可只有两个方程,因此可能有很多组解。按题意:men、women、children都应为正整数,且它们的取值范围应为:
men:0~8
women:0~11
children:0~36(children必须是偶数)
要想得到多种men、women、children组合,只需在上述三个数的范围之内找到合适的men、women、children的值并满足上述方程组即可。
求解本题的一个自然想法是,依次对men、women、children取值范围内的各数进行一一试探,找到满足前面两方程的组合:首先从0开始,列举men的各种可能值,在每个men值下找到满足两个方程的一组解,算法如下:
men=0;
while(men++<8)
{
s1:找满足两个方程的解的women、children;
s2:输出一组解;
}
下面进一步用穷举法来实现s1:
women=0;
while(women++<11)
{
s1.1:找满足方程的一个children;
s1.2:输出一组解;
}
由于对所列举的每个men和women都可以按下式
children=36-men-women
求出一个children。因此只要该children满足另一个方程
4*men+3*women+children/2=36
便可以得到一组满足题意的men、women、children解。为保证children必须是偶数,则应是children/2.0,而不应是children/2,故s1.1与s1.2可以改写为:
children=36-men-women;
if((4*men+3*women+children/2.0)==36)
{
printf("\nmenis%d",men);
printf("\nwomenis%d",women);
printf("\nchildrenis%d",children);
}经过几步求解之后,可写出如下程序:
main()
{
intmen=0,women,children;
while(men++<8)
{
women=0;
while(women++<11)
{
children=36-women-men;
if((4*men+3*women+children/2.0)==36)
{
printf("\nmenis%d",men);
printf("\nwomenis%d",women);
printf("\nchildrenis%d",children);
}
}
}
}
程序运行结果如下:
menis3
womenis3
childrenis30
例5.14
填写程序。打印所有符合下列条件的3位正整数:是某一个数的平方数,其中有两位数字相同,如100、121等。
main()
{inta,b,c,n,k;
for(k=10;;k++)
{n=[1]; /*计算某数的平方数*/
if(n>999)[2]; /*如果不是3位正整数就退出*/
a=n/100; /*计算百位数*/
b=[3]; /*计算十位数*/
c=n%10; /*计算个位数*/
if(!((a-b)*(b-c)*(c-a))) /*判断3位数a、b、c中是否有两个 数相同*/
printf("\n%d,%d",n,k);
}
}
问题分析:本程序最终的目的是得到某数及某数的平方数(3位正整数)。对于3位正整数来说,最小的是100,而最大的是999,从程序中可以看出变量k就是某数,而n就是k的平方数。
图5.6加密循环示意图第一个空中很显然应该填k*k;第2个空是在n>999的条件下才执行的,根据题意知:只有当k的平方数不是3位数(大于999)时,应该退出循环,由此可得第2个空应填break;从a=n/100可知变量a中存放的是3位正整数n的百位数,由c=n%10可知,变量c中存放的是n的个位数,由此第3个空中应该计算得到十位数,所以第3个空应填n/10%10或(n-a*100)/10。所以答案应为:[1]k*k;[2]break;[3]n/10%10或(n-a*100)/10。5.6.3密码问题
在报文通信中,为了使报文保密,发报人经常要按一定的规律将其加密,收报人按同样的规律将其解密。
最简单的加密方法是,将报文中的每一个英文字母转换为其后的第h个字母。例如,当h=4时,字母a转换成e,B转换成F等。这种转换在C语言中比较方便,只要将该字母的ASCII码加上4(变量h的值)即可。在转换时,如果某小写字母其后的第h个字母已经超出小写字母z,或某大写字母其后的第h个字母已经超出大写字母Z,则将循环到字母表的开始。例如,小写字母w转换为a,大写字母Z转换为D等,如图5.6所示。根据加密的过程可以知道解密的过程:将密文字母ASCII码减去h的值,就可以恢复原文字母。
例5.15
从键盘输入一行字符(明文),将其中的英文字母进行加密输出(非英文字母不用加密)。如“China!”转换成“Glmre!”。
程序如下:
#include<stdio.h>
main()
{inth;
charc;
printf("Pleaseinputh:");
scanf("%d",&h);
scanf("%c",&c); /*消掉上一行输入时的回车符*/
while((c=getchar())!=‘\n’) /*输入的字符是回 车符就结束*/
{if((c>=‘a’&&c<=‘z’)||(c>=‘A’&&c<=‘Z’))/*如果是字母才 处理*/
{c=c+h;
if(c>‘Z’&&c<=‘Z’+h||c>‘z’)c=c-26; /*如果超过范围, 则从头开始*/
}
printf("%c",c);
}
}程序运行结果:
Pleaseinputh:4
China!
Glmre!
例5.16
从键盘输入一行经过加密的字符(密文),将其中的英文字母进行解密输出(非英文字母不用解密)。
程序如下:
#include<stdio.h>
main()
{inth;
charc;
printf("Pleaseinputh:");
scanf("%d",&h);
scanf(“%c”,&c); /*消掉上一行输 入时输入的回车符*/
while((c=getchar())!=‘\n’) /*输入的字符是 回车符就结束*/
{if((c>=‘a’&&c<=‘z’)||(c>=‘A’&&c<=‘Z’)) /*如果是 字母才处理*/
{c=c-h;
if(c<‘a’&&c>=‘a’-h||c<‘A’)c=c+26; /*如果超过范 围,则从头开始*/
}
printf("%c",c);
}
}
程序运行结果:
Pleaseinputh:4
Glmre!
China!5.6.4方程求根问题
1.二分法求方程实根
二分法或称对分法是求方程近似解的一种最简单、最直观的方法。
设函数f(x)在区间[a,b]上单调连续,且f(a)f(b)<0(区间两端点的函数值f(a)和f(b)异号),则方程f(x)=0在(a,b)内有惟一的实根x*。这是微积分中的中值定理,也是使用二分法的前提条件。
1)算法思想
二分法是用对分区间的方法根据分点处函数f(x)值的符号,逐步将有根区间缩小,使在足够小的区间内,方程有且仅有1个实根。
首先,为便于讨论不妨设f(a)<0,f(b)>0,如图5.7所示。
图5.7二分法思想取区间[a,b]的中点x0= ,计算函数值f(x0),若正好f(x0)=0,则说明已经找到方程的根x*= 。否则f(x0)与f(a)异号,或与f(b)异号。若f(x0)>0,则f(x0)与f(a)异号,记a1=a,b1= (即只取原来区间的左半部分);若f(x0)<0,则f(x0)与f(b)异号,记a1= ,b1=b(即只取原来区间的右半部分)。这样区间[a1,b1]是方程新的有根区间,此区间被包含在旧的有根区间[a,b]内,且长度是原区间长度的一半,即b1-a1= 。再将区间[a1,b1]对分,得中点x1= ,计算函数值f(x1),重复上述过程,则可得到长度又缩小一半的有根区间[a2,b2],如此反复对分下去,可得到一系列的有根区间:[a,b],[a1,b1],[a2,b2],…,[ak,bk],…。其中,每个区间都在前一区间内,且长度是前一区间的一半,因此区间[ak,bk]的长度为
bk-ak=
=
=
每次对分后,如果取有根区间[ak,bk]的中点xk=作为f(x)=0的近似根,则在对分过程中将得到一系列的近似根:
x0,x1,x2,…,xk,……
2)二分法程序流程图
二分法求方程实根算法的流程图见图5.8所示。
图5.8二分法程序流程图
3)程序实例
例5.17
用二分法求非线性方程x2-6x-1=0在区间[2,8]上的实根。
程序如下:
#include<stdio.h>
#include<math.h>
#defineepsilon0.00001 /*定义要求的精度*/
floatf(floatx) /*求函数值*/
{return(x*x-6*x-1);
}
main()
{floata,b,x;
scanf(“%f%f”,&a,&b); /*输入求根区间*/
if(f(a)*f(b)>=0) /*判断是否符合二分法使 用的条件*/
{printf(“不满足二分法使用条件,退出!”);exit(0);}
do
{x=(a+b)/2;
if(f(x)*f(b)<0) /*如果成立,则根在区间 的右半部分*/
a=x;
else /*否则根在左半部分*/
b=x;}
while(fabs(b-a)>=epsilon); /*判断是否达到精度要求,如果没达到,则继续循环*/
x=(b+a)/2; /*取最后的小区间中点作为根的近似值*/
printf("x=%f\n",x); /*输出函数的近似根*/
}
程序运行结果:
28
x=6.1662276
2.迭代法求方程实根
设非线性方程为f(x)=0,用迭代法求一个实根的基本方法如下:
首先将方程f(x)=0改写成便于迭代的格式
x
=f(x)
然后初步估计方程实根的一个初值x0,按下面的公式进行迭代:
(k=0,1,2,…)
直到满足条件
或者迭代了足够的次数还不满足这个条件为止。其中e为事先给定的精度要求。
1)迭代式构造的条件
迭代法在迭代过程中能否得到满足条件的方程的实根,取决于根据原方程f(x)=0构造的迭代式(k=0,1,2,…)在迭代过程中得到的一系列近似根(x1,x2,x3,…)是否趋近于(收敛)真实的根。只要迭代式中迭代函数满足
≤L(0<L<1)
就可以使用迭代法得到一个实根。
2)迭代法程序流程图
迭代法程序流程图如图5.9所示。
3)程序举例
例5.18
用迭代方程xn+1=(lg(xn)+7)/2计算近似根。
程序如下:
#include<stdio.h>
#include<math.h>
#defineN100 /*最大迭代次数,目的为防止迭代公式发散*/
图5.9迭代法程序流程图
#defineepsilon0.00001 /*要求的精度*/
floatfi(floatx) /*定义计算函数值的函 数*/
{
return((log10(x)+7)/2);
}
main()
{
floatx0,x1,d; /*变量c为要求的精度,变量d为计算得的 误差*/
intk=1; /*迭代次数变量赋初值*/
printf(“Pleaseinputx0:”);
scanf(“%f”,&x0); /*输入近似根的初值和要求的 精度*/
do
{x1=fi(x0); /*迭代*/
if(k++>N) /*如果达到最大迭代次数,表 明迭代公式发散*/
{printf("Methodfailed!\n");
exit();
}
d=x1-x0;
x0=x1;
}
while(fabs(d)>=epsilon); /*如果未达到要求的精度,将继续 循环*/
printf(“\nTherootoftheequationisx=%f\n”,x1);/*输出近似 根*/
}
程序运行结果:
Pleaseinputx0:1.0
Therootoftheequationisx=3.789278
3.牛顿法求方程实根
牛顿迭代法是一种逐步线性化方法,即将非线性方程f(x)=0的求根问题归结为计算一系列线性方程的根。
设xk是方程f(x)=0的一个近似根,将f(x)在xk处作一阶泰勒展开,即
f(x)≈f(xk)
+
f'(xk)(x-xk)
于是得到如下的近似方程:
f(xk)
+
f'(xk)(x-xk)
=
0
设f'(xk)≠0,则上式的解为
取x作为原方程新的近似根xk+1,即令
k
=
0,1,2,…
则称上式为牛顿迭代公式。用牛顿迭代公式求方程近似根的方法称为牛顿迭代法,简称牛顿法。
1)牛顿法的几何意义
方程y=f(xk)+f'(xk)(x-xk)是曲线在点Pk(xk,f(xk))处的切线方程。迭代公式中的xk+1就是切线与x轴交点的横坐标,所以牛顿法就是用切线与x轴交点的横坐标近似代替曲线与x轴交点的横坐标。故牛顿法又称切线法,其几何意义见图5.10。
图5.10牛顿法几何意义
2)牛顿法算法流程图
牛顿法算法流程图如图5.11所示。
3)程序举例
例5.19
用牛顿法求非线性方程x2-x-2
=
0在1.0附近的实根。
#include<stdio.h>
#include<math.h>
图5.11牛顿法算法流程图
#defineN100 /*最大迭代次数,如果达到最大迭代次数仍找不到满足精度要求的根,则说明精度要求太高或没有根*/
#defineepsilon0.0001 /*控制精度*/
floatf(floatx) /*求f(x)的值*/
{return(x*x-x-2);}
floatf1(floatx) /*求f'(x)的值*/
{return(2*x-1);}
main()
{inti;
floatx0,x1;
printf("Pleaseinputinitx0:");
scanf("%f",&x0); /*输入初始根x0*/
x1=x0;
for(i=0;i<N;i++)
{if(f1(x0)==0) /*如果f'(x0)的值为0,则无实根或 x0选择不合适*/
{printf("f'(x)=0,nosolved!\n");
return;
}
printf("x(%d)=%f\n",i,x1); /*输出每次迭代的近似根*/
x1=x0-f(x0)/f1(x0); /*牛顿法迭代公式*/
if(fabs(x1-x0)<epsilon||fabs(f(x1))<epsilon)/*如果满足精度 要求*/
{printf("\nTherootoftheequationisx=%f\n",x1);/*输出近 似根*/
return;
}
x0=x1;/*将最新一次得到的近似根作为下次迭代的 x0*/
}
printf("After%drepeate,nosolved!\n",N);/*超过迭代次数还 未找到实根*/
}程序运行结果:
Pleaseinputinitx0:1.0
x(0)=1.000000
x(1)=3.000000
x(2)=2.200000
x(3)=2.011765
x(4)=2.000046
Therootoftheequationisx=2.0000005.6.5*梯形法求定积分问题
设定积分为
由积分的知识可以知道,该积分值的几何意义是在区间[a,b]内的曲线f(x)下的面积(即由曲线AB、直线Aa、直线ab、直线Bb所围成的区域的面积),如图5.12所示。
如果用梯形区域ABba的面积近似代替曲边AB与x轴之间的面积,即可得定积分的近似值。这种方法称为求定积分的梯形法。
图5.12定积分的几何意义
1.梯形法求定积分公式
采用梯形面积近似作为积分值,因此
上式就是求定积分的梯形公式。但是这种方法求得的近似值误差比较大,因此,通常情况下用于求定积分的梯形公式是复化梯形公式。
2.复化梯形求定积分公式
复化梯形求定积分的基本思想是:
首先将积分区间[a,b]进行n等分,得到n个小区间[xi,xi+1](i=0,1,2,…,n-1),每个小区间的长度为h=(b-a)/n,如图5.13所示,其中xi=a+i*h。
然后在每个小区间上用梯形公式求积分,即
图5.13复化梯形求定积分的几何意义最后将所有小区间的积分累加起来就可以得到整个积分区间的定积分了,即
复化梯形公式的算法流程图如图5.14所示。
图5.14复化梯形公式流程图
例5.20
用复化梯形法求定积分
即a=0,b=1,f(x)=e-x。
程序如下:
#include<stdio.h>
#include<math.h>
main()
{intn,k;
doublea=0.0,b=1.0,h,T,p,x;
printf("Pleaseinputn:");
scanf("%d",&n); /*输入等分数*/
h=(b-a)/n;
T=h*(exp(-a)+exp(-b))/2; /*用梯形公式计算积分近 似值*/
p=0.0;
for(k=1;k<n;k++) /*根据区间等分数,修正 积分值*/
{x=a+k*h;p=p+exp(-x);}
T=T+p*h; /*将积分近似值与修正值之和作为复化梯形公式求得的积分值*/
printf("T=%f\n",T);
}
在程序执行时,提示如下:
Pleaseinputn:
此时要求从键盘输入等分数n,如果输入100,则输出结果为
T
=
0.632126
如果输入10,则输出结果为
T
=
0.632647
从运行结果可以看出,对于不同的等分数,输出的结果(即积分的近似值)是不相同的。理论上说,等分数越多,其精度越高。
一、选择题
1.以下关于语句的说法中正确的是()。
A.
do-while语句的循环体至少会被执行一次
B.
while语句的循环体至少会被执行一次
C.
for语句的循环体至少会被执行一次
D.在C语言中只能用do、do-while或for语句实现循环结构习题5
2.
C语言用()表示逻辑“真”值。
A.
true B.
t或y C.
整数1 D.整数0
3.以下for循环是()。
for(x=0,y=0;(y!=123)&&(x<4);x++);
A.无限循环
B.循环次数不定
C.执行4次
D.执行3次
4.
C语言中()。
A.不能使用do-while语句构成循环
B.
do-while语句构成的循环必须用break语句才能退出
C.
do-while语句构成的循环,当while语句中的表达式值为非零时结束循环
D.
do-while语句构成的循环,当while语句中的表达式值为零时结束循环
5.
while语句和do-while语句的主要区别是()。
A.
do-while的循环体至少无条件执行一次
B.
while的循环控制条件比do-while的循环控制条件严格
C.
do-while允许从外部转到循环体内
D.
do-while的循环体不能是复合语句
6.以下程序的输出结果是()。
#include<stdio.h>
main()
{
inti;
for(i=1;i<=5;i++)
{if(i%2)printf("*");
elsecontinue;
printf("#");
}
printf("$\n");
}
A.
*#*#*#$
B.
#*#*#*$ C.
*#*#$ D.
#*#*$
7.执行语句“for(s=0,i=1;i<=10;i=i+2)s+=i;”后,变量s、i的当前值分别是()。
A.
25,11
B.
12,10
C.
25,10
D.
12,11
8.以下程序的输出结果是(
)。
main()
{inta=0,i;
for(i=1;i<5;i++)
{switch(i)
{case0:
case3:a+=2;
case1:
case2:a+=5;
default:a+=5;
}
}
A.
13
B.
37 C.
10 D.
20
9.下面的程序段可循环的次数为()。
A.
2次 B.
1次
C.
0次 D.死循环,无数次
inti=2;
while(i=0)printf("%d",i),i--;
printf("\n");
10.下面的程序运行后(
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年四川中建三局装饰有限公司招聘笔试参考题库含答案解析
- 2025年国网电力科学研究院武汉南瑞有限责任公司招聘笔试参考题库附带答案详解
- 2025-2030全球高压有载分接开关行业调研及趋势分析报告
- 2025年全球及中国医用 PTFE 管行业头部企业市场占有率及排名调研报告
- 2025年度店铺债权债务转让合同范本
- 2025年度店铺租赁权转让及装修设计咨询服务合同3篇
- 二零二五年度车库购置与物业管理合作协议4篇
- 2024铁路货运合同铁路运输货物交付与验收协议3篇
- 二零二五年度餐厨废弃物处置与废弃物处理设施改造合同3篇
- 2025年度个人与个人草原生态修复工程合同范本
- 南通市2025届高三第一次调研测试(一模)地理试卷(含答案 )
- 2025年上海市闵行区中考数学一模试卷
- 2025中国人民保险集团校园招聘高频重点提升(共500题)附带答案详解
- 重症患者家属沟通管理制度
- 法规解读丨2024新版《突发事件应对法》及其应用案例
- IF钢物理冶金原理与关键工艺技术1
- 销售提成对赌协议书范本 3篇
- 劳务派遣招标文件范本
- EPC项目阶段划分及工作结构分解方案
- 《跨学科实践活动4 基于特定需求设计和制作简易供氧器》教学设计
- 信息安全意识培训课件
评论
0/150
提交评论