循环知识小结.docx
- 文档编号:27732071
- 上传时间:2023-07-04
- 格式:DOCX
- 页数:19
- 大小:231.45KB
循环知识小结.docx
《循环知识小结.docx》由会员分享,可在线阅读,更多相关《循环知识小结.docx(19页珍藏版)》请在冰豆网上搜索。
循环知识小结
循环知识小结
一、有关循环的语法
1.while语句
2.do~while语句
3.for语句
4.break语句
5.continue语句
二、循环语句的比较
⏹循环方式
☐while语句和do~while语句多采用标记式循环
⏹用于循环次数不定的情况
☐for语句更方便对于循环次数确定的情况
⏹循环结构
☐for语句和while语句先判断循环控制条件,后执行循环体
⏹可能一次也不执行循环体
☐do~while语句是先执行循环体,后进行循环控制条件的判断
⏹do~while语句至少执行一次循环体
⏹do~while语句更适合于第一次循环肯定执行的场合
☐实现功能
⏹do~while和while语句只有一个表达式,用于控制循环是否进行
⏹for语句有三个表达式
☐可以控制循环是否进行,并能为循环变量赋初值及不断修改循环变量的值
☐for语句比while和do~while语句功能更强,更灵活
☐语句形式
⏹初始值
☐while、do~while循环时,循环变量的初始值操作应放在while和do~while语句之前完成
☐for语句通常在表达式1中实现循环控制变量的初始化
⏹while和for表达式的括号后面没有“;”
⏹do~while表达式的括号后面有“;”
⏹循环语句的选用原则
☐循环次数是否确定
⏹循环次数已知,一般用for语句
⏹循环次数由循环体的执行情况来确定,一般采用while语句或do~while语句
☐循环体是否一定执行
⏹循环体至少要执行一次时,采用do~while语句
⏹循环体可能一次也不执行,则选用while语句或for语句
三、关于嵌套循环
1.在嵌套的各层循环中,应使用复合语句保证逻辑上的正确性
2.嵌套循环的内层和外层的循环控制变量不应同名,以免造成混乱
3.嵌套循环最好采用右缩进格式书写,以保证层次的清晰性
4.循环嵌套不能交叉,即在一个循环体内必须完整地包含另一个循环
5.在多层循环中,应将最忙(循环次数最多)的循环放在最内层,以减少CPU切入循环的次数
四、有关循环应用的讨论
1、国王的许诺。
相传国际象棋是古印度舍罕王的宰相达依尔发明的。
舍罕王十分喜欢象棋,决定让宰相自己选择何种赏赐。
这位聪明的宰相指着8×8共64格的象棋盘说:
陛下,请您赏给我一些下麦子吧,就在棋盘的第1格子中放1粒,第2格中放2粒,第3格中放4粒,以后每一格都比前一格增加一倍,依此放完64个格子,我就感恩不尽了。
舍罕王让人扛来一袋麦子,他要兑现他的许诺。
请问:
国王他能兑现他的许诺吗?
请编程计算舍罕王共要多少麦子赏赐他的宰相,这些麦子合多少立方米(已知1立方米麦子约为1.42e8粒)?
问题分析:
这是一个典型的循环次数已知的等比数列求和问题。
第1格放1粒,第2格放2粒,第3格放4=22粒……第i格放2i-1粒。
所以,总粒数为sum=1+2+22+23+……+263。
对于这样的问题,我们采取的策略是每次加一个累加项,用循环语句重复执行64次累加运算,即可求出累加和sum。
在累加求和问题中,寻找累加项的构成规律是问题求解的关键。
一般地,寻找累加项构成规律有两种方法:
一种是寻找统一的累加项表示规律,即用一个通式来表示累加项;另一种是寻找前后项之间的统一的变化规律,即利用前项得到后项的表示。
该题用第一种方法,可得累加项的通式为term=2n-1,即term=pow(2,n-1),n从1变化到64,即从第一项开始计算累加和,所以有sum=sum+term,sum的初始值为0。
因此得源代码如下:
#include
#include
#defineCONST1.42e8
voidmain()
{intn;
doubleterm,sum=0;
for(n=1;n<=64;n++)
{term=pow(2,n-1);
sum=sum+term;
}
printf("麦子总粒数sum=%e\n",sum);
printf("麦粒体积volum=%e(立方米)\n",sum/CONST);
}
用第二种方法分析得知,后项总是前项的两倍,于是得到累加项通式为:
term=term*2,term的初值为1,即从第二项开始计算累加项term,并进行63次累加计算,所以有sum=sum+term,sum初值为1。
一般情况下,sum的初值都置0,在此是因为事先将累加的第一项加到sum中,所以才有sum=1。
用累乘trem=term*2计算通项,显然比直接计算2n-1的效率高得多,在程序设计中可经常使用这种技巧。
用第二种方法设计的源代码如下:
#include
#defineCONST1.42e8
voidmain()
{intn;
doubleterm=1,sum=1;
for(n=2;n<=64;n++)
{term=term*2;
sum=sum+term;
}
printf("麦子总粒数sum=%e\n",sum);
printf("麦粒体积volum=%e(立方米)\n",sum/CONST);
}
以上程序运行的结果为:
这种数值如此庞大,是舍罕王绝对没有预料到的,它相当于全世界若干世纪的全部小麦,看来舍罕王是无法兑现自己的诺言了!
2、马克思手稿中的数学问题。
马克思手稿中有一道趣味数学题:
有30个人,其中有男人、女人和小孩,在一家饭馆里吃饭共花了30先令,每个男从各花3先令,每个女人各花2先令,每个小孩各花1先令,问男人、女人和小孩各有几个人?
问题分析:
设男人、女人和小孩各x、y、z人,按题目要求可得到下面的方程:
x+y+z=30
3x+2y+z=50
两个方程有三个未知数,因此这是一个不定方程,有多组解,用代数方法很难求解,一般采用“穷举法”求解该类问题。
所以“穷举法”(也称“枚举法”)就是将所有可能的方案都逐一测试,从中找出符合指定要求的答案。
如果由人工来进行这样的求解过程,工作量不可想象,而由计算机来完成却十分简单。
穷举法是计算机程序设计中最简单、最常用的一种方法,它充分利用了计算机处理速度高的特性。
使用穷举法的关键是要确定正确的穷举范围,过分扩大会导致程序运行效率的降低,过分缩小会遗漏正确的结果而导致错误。
方法一采用三重循环穷举x、y、z的全部可能的组合。
源代码如下:
#include
voidmain()
{intx,y,z;
printf("Man\tWomen\tChildren\n");
for(x=0;x<=30;x++)
for(y=0;y<=30;y++)
for(z=0;z<=30;z++)
if((x+y+z==30)&&(3*x+2*y+z==50))
printf("%3d\t%5d\t%8d\n",x,y,z);
}
实际上,由于每个男人花3先令,所以在只花50先令的情况下,最多只有16个男人;同样,在只花50先令的情况下,最多只有25个女人,而小孩的人数可以由方程式x+y+z=30得到,因此可将需要穷举的范围缩小。
方法2改进算法。
#include
voidmain()
{
intx,y,z;
printf("Man\tWomen\tChildren\n");
for(x=1;x<=16;x++)
for(y=1;y<=25;y++)
{z=30-x-y;
if(3*x+2*y+z==50)
printf("%3d\t%5d\t%8d\n",x,y,z);
}
}
以上程序运行的结果为:
3、编程计算一元二次方程ax2+bx+c=0的根,a,b,c由键盘输入,其中a≠0。
程序设计时,根据一元二次方程求根公式把所有的可能考虑进来,设计算法如下:
#include
#include
#include
#defineEPS1e-6
voidmain()
{floata,b,c,disc,p,q;
printf("Pleaseinputa,b,c:
");
scanf("%f,%f,%f",&a,&b,&c);
if(fabs(a)<=EPS)//测试a是否为0,避免发生除0错误
{printf("不是一元二次方程\n");
exit(0);//终止整个程序的执行,强制返回操作系统
}
disc=b*b-4*a*c;
if(fabs(disc)<=EPS)//实数disc与0相比较
printf("该方程有两个相等的实根:
x1=x2=%.2f\n",-b/(2*a));
else
{p=-b/(2*a);
q=sqrt(fabs(disc))/(2*a);
if(disc>1e-6)
printf("该方程有两个不等的实根:
x1=%.2f,x2=%.2f\n",p+q,p-q);
else
{printf("该方程有两个共轭复根:
\n");
printf("x1=%.2f+%.2fi\n",p,q);
printf("x2=%.2f-%.2fi\n",p,q);
}
}
}
程序运行的结果测试如下:
测试1:
测试2:
测试3:
测试4:
补充说明:
1)函数exit()
其作用是终止整个程序的执行,强制返回操作系统。
和goto,break,continue等控制语句类似,可以用于控制程序的流程。
当程序执行的必需条件不能满足时,常用exit()函数终止程序的执行。
调用该函数需要包含头文件
函数exit()的一般调用格式为:
exit(code);
参数code为int型。
当code值为0或为宏常量EXIT_SUCCESS时,表示程序正常退出;当code值为非0或为宏常量EXIT_FAILURE,表示程序出现某种错误后退出。
2)实数不能直接和0比较相等与否
本例中由于a是用户输入的原始数据,不存在计算误差,因此a与0的比较也可以用a==0代替。
但因disc变量是经过计算得到的浮点数,而绝大多数计算机中表示的浮点数都只是它们在数学上表示的数据的近似值,因此disc与0的比较不能用disc==0来代替,必须用fabs(disc)<=EPS表示。
3)有关实型数据的输入格式
实型变量若定义为float类型,则在用scanf()函数输入时应用%f格式;若定义为double类型,则应用%lf格式输入。
实际从键盘输入的数据可以是整数、小数或指数形式,存入变量对应的存储空间均为指数形式。
在C中所有实型常量的类型默认为double类型,若将一个实型常量赋值给float型变量时,系统会出现警告,提醒用户由于两种类型的有效数字位数不同,容易产生精度损失问题,如果所处理的数据有效数字位数在7位以内,此警告可忽略。
4、从键盘输入一个正整数,编程判断它是否是素数。
若是素数,输出“Yes!
”,否则输出“No!
”
问题分析:
所谓“素数”即质数,是只能被1和本身整除的数。
所以判素数的方法:
把m作为被除数,穷举2~m-1之间的数作为除数,若其中有一个能整除,即可确定m不是素数,否则是素数。
事实上,根本用不着除那么多次,用数学的方法可以证明:
只需要用2~
之间的整数去除m,即可得到正确的判定结果。
方法一用goto语句实现的程序如下:
#include
#include
voidmain()
{intm,i,k;
printf("请从键盘输入一个正整数:
");
scanf("%d",&m);
k=(int)sqrt(m);
for(i=2;i<=k;i++)
{if(m%i==0)
{printf("%d不是素数!
\n",m);
gotoend;
}
}
printf("%d是素数!
\n",m);
end:
printf("程序结束!
\n");
}
方法二用break语句实现的程序如下:
#include
#include
voidmain()
{intm,i,k;
printf("请从键盘输入一个正整数:
");
scanf("%d",&m);
k=(int)sqrt(m);
for(i=2;i<=k;i++)
if(m%i==0)
break;
if(i>k)
printf("%d是素数!
\n",m);
else
printf("%d不是素数!
\n",m);
}
分析:
goto语句可以控制流程跳转到程序中任意某个指定的语句处去执行,而break语句的作用是终止整个循环的执行,从循环体内中途退出,接着去执行循环语句之后的第一条语句。
break语句的使用使循环的控制更灵活了。
使用break语句的副作用是它会使循环体本身形成两个出口,同goto语句相比,只不过break语句跳转的距离和方向受到了严格的限制,而不像goto语句那样可以向任意方向跳转。
因此,无论使用goto语句还是break语句,都不是一种好的选择,所以应尽量少用或不用它们。
很多情况下,可以采用标志变量并加强循环测试的方法是完全可以避免使用break语句的。
方法三通过设置标志变量并加强循环测试的方法实现程序如下:
#include
#include
voidmain()
{intm,i,k,flag=1;
printf("请从键盘输入一个正整数:
");
scanf("%d",&m);
k=(int)sqrt(m);
if(m<2)//2以下的数不是素数
flag=0;
for(i=2;i<=k&&flag;i++)
if(m%i==0)
flag=0;
if(flag)
printf("%d是素数!
\n",m);
else
printf("%d不是素数!
\n",m);
}
结论:
从程序的可读性方面看,方法三比方法一和方法二都好!
5、从键盘输入一个正整数m,若m不是素数,则打印其所有因子;否则,打印“没有因子,是素数!
”
问题分析:
能被m整除的数i就是m的因子,因此当m%i==0时,不退出循环而打印当时的i值即可。
为了得到m的所有因子,循环变量i应从2一直变化到m-1,即无论m是否是素数都要检验所有的i值。
#include
voidmain()
{intm,i,flag=1;
printf("请从键盘输入一个正整数:
");
scanf("%d",&m);
for(i=2;i<=m-1;i++)//此处的m-1可否改为m/2或sqrt(m)?
if(m%i==0)
{flag=0;
printf("%d\n",i);
}
if(flag)
printf("%d是素数,没有因子!
\n",m);
}
五、有关getchar()、getche()和getch()的讨论
⏹getchar()函数采用缓冲输入方式,即输入字符先被放到缓冲队列中,直到键入回车键时才返回,getcahr()每次从输入缓冲队列中读取第一个字符进行相应的处理。
⏹getch()函数在击键之后立即返回,无需输入回车键,且不向屏幕回显键入的字符。
⏹getche()函数功能同getch()函数,只是前者要向屏幕回显键入的字符。
⏹getch()和getche()是TurboC特有的库函数,在头文件conio.h中定义。
例1:
从键盘输入一个班学生(人数不确定)一门课程的五分制成绩,编程要求每输入一个五分成绩,就显示其所在的分数段,同时,统计并打印每种成绩的人数。
问题分析:
对于这类输入数据个数不确定的问题,常常采用输入一个特殊的数作为程序判断循环结束标志的方法。
例如,输入百分制成绩时,用负数作为输入结束的标志,输入五分制成绩里,则可用一个特殊的符号作为输入结束的标志。
程序如下:
#include
voidmain()
{
intaCount=0,bCount=0,cCount=0,dCount=0,eCount=0;//定义5个计数器并置0
chargrade;
printf("请输入成绩等级字母,并以'#'号结束:
\n");
grade=getchar();
while(grade!
='#')
{
switch(grade)
{
case'A':
case'a':
printf("90--100\n");
aCount++;
break;
case'B':
case'b':
printf("80--89\n");
bCount++;
break;
case'C':
case'c':
printf("70--79\n");
cCount++;
break;
case'D':
case'd':
printf("60--69\n");
dCount++;
break;
case'E':
case'e':
printf("<60\n");
eCount++;
break;
default:
printf("输入错误!
\n请重新输入:
\n");
}
grade=getchar();
}
printf("统计结果:
A:
%d,B:
%d,C:
%d,D:
%d,E:
%d\n",aCount,bCount,cCount,dCount,eCount);
}
运行结果如下:
测试1:
问题:
只有输入#并回车才能真正结束程序,#没有直到预期的目的。
测试2:
问题:
以回车或空格作为每个等级的分隔符,统计结果虽然正确,但都会提示出错信息,
解决方法一:
在switch语句中增加一个case分支:
case'':
case'\n':
break;
解决方法二:
将接收字符的操作改用scanf函数实现,并在%c格式前增加一个空格,将前面输入数据输入时存于缓冲区的回车符读入,避免被后面的字符型变量作为有效字符读入。
scanf("%c",&grade);
例2:
设计一个简单的计算器程序,要求用户可以连续做多次算术运算,每次运算结束后,程序都会给出提示:
Doyouwanttocontinue(Y/Nory/n)?
如果用户输入Y或y时,程序继续执行其他运算,否则退出程序。
程序如下:
#include
voidmain()
{
intd1,d2;
charop,reply;
do
{
printf("请输入计算表达式:
");
scanf("%d%c%d",&d1,&op,&d2);
switch(op)
{case'+':
printf("%d%c%d=%d\n",d1,op,d2,d1+d2);break;
case'-':
printf("%d%c%d=%d\n",d1,op,d2,d1-d2);break;
case'*':
printf("%d%c%d=%d\n",d1,op,d2,d1*d2);break;
case'/':
if(d2==0)
printf("除数不能为0\n");
else
printf("%d%c%d=%d\n",d1,op,d2,d1/d2);
break;
default:
printf("运算符错误!
\n");
}
printf("Doyouwanttocontinue(Y/Nory/n)?
");
reply=getchar();
}while(reply=='Y'||reply=='y');
printf("程序结束!
\n");
}
问题:
测试不能得到预期的结果,原因在于函数getchar的行缓冲问题导致getchar()把用户输入表达最后的回车符作为其读入字符。
解决办法:
将语句reply=getchar();改为reply=getch();或reply=getche();或scanf("%c",&reply);都可以。
六、结构化程序设计的核心思想
结构化程序设计是一种进行程序设计的原则和方法,按照这种原则和方法设计的程序具有结构清晰、容易阅读、容易修改、容易验证等特点。
因此,人们把“结构清晰、容易阅读、容易修改、容易验证”作为衡量程序质量的首要条件。
也就是说,所谓“好”的程序是指“好结构”的程序,一旦效率与“好结构”发生矛盾时,那么宁可在可容忍的范围内降低效率,也要确保好的结构。
结构化程序设计的基本核心思想归纳起来为以下3点:
(1)采用顺序、选择、循环三种基本结构作为程序设计的基本单元。
(2)尽量不要使用多于一个的goto语句标号,同时只允许在一个“单入口单出口”的模块内用goto语句向前跳转,不允许回跳。
(3)采用“自顶向下、逐步求精”和模块化方法进行结构化程序设计。
七、循环的应用
(一)递推算法
1.编程计算1+2+3+……100的值。
2.编程计算1×2×3+3×4×5+5×6×7+……+99×100×101的值
[提示:
用累加和算法,通项公式为term=i*(i+1)*(i+2)(i=1,3,5,…,99),或者公式为term=(i-1)*i*(i+1)(i=2,4,6,…,100),步长为2。
]
2.编程计算n!
=1×2×3×……×n的值。
3.编程计算1!
+2!
+3!
+……+10!
的值。
[提示:
用累加和算法,累加项为term=term*i(i=1,2,3,…,10),term的初始值为1。
]
4.编程计算a+aa+aaa+……+aa…a(n个a)的值,n和a由键盘输入。
[提示:
用累加和算法,累加项为term=term*10+a(i=1,2,3,…,n),term初始值为0。
]
5.编程计算分数数列
前20项之和。
6.编程计算
,n由键盘输入。
如n为11时,s=1.83333。
7.编程计算
。
8.编程计算xn,其中x和n均由键盘输入。
9.一球从200米高度自由落下,每次落地后反跳回原高度的一半,再落下。
编程求它第10次落地时共经过的路程及第10次落地后反弹的高度。
10.猴子吃桃问题。
11.求∏的近似值。
12.求Fibonacci数列。
(二)穷举算法
1.请编程判断一个正整数m(m>=3)是否是素数。
2.编程实现从键盘上输入整数m和k,输出大于且紧靠m的k个素数。
3.请编程找出1至99之间的全部同构数。
所谓同构数是这样的一
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 循环 知识 小结