第5章 循环结构
面对重复问题时,为了避免工作量大,程序冗长、重复,难以阅读和维护,几乎每一种高级语言都提供了循环控制,来处理需要进行的重复操作。
一、while循环
基本架构
while(condition)//条件判断
{
statement(s);//循环体;(可以为空)
}
condition可以是任意表达式。当为任意非零值时都为ture,当条件为ture时执行循环。当条件为false时,退出循环。
statement(s)可以是一个单独的句子,也可以是几个语句组成的代码块,也可为空。
只要当循环条件表达式为真(即给定条件成立),将执行循环体语句。
特点:先判断条件表达式,后执行循环体语句。所以可能一次都不执行。
流程图:
二、do...while语句
基本架构:
do
{
statement(s);
}while(condition);
特点:条件表达式出现在循环的尾部,所以循环中表达式在条件被测试前至少被执行了一次。
流程图:
三、for语句
允许编写一个执行指定次数的虚幻控制结构。
基本架构:
for(init;condition;increment)//(循环变量赋初值;循环条件;循环变量增值)
{
statement(s);
}
运行过程:
init会首先被执行,且只会执行一次。允许声明并初始化任何循环控制变量。可写多个(用逗号隔开),也可以不写任何语句(在循环语句之前,给循环变量赋初值),只要有一个分号即可。
判断condition(不可写多个)。是否执行主体statement。
condition 一般为关系表达式,逻辑表达式,也可以是判断语句,数值表达式或字符表达式,只要值为非零,就执行循环体。
跳回increment语句,该语句允许更新循环控制变量。可留空(写在循环体中),只要在condition条件后有一个分号即可,也可写多个(用逗号隔开)。
条件再次被判断。如果为真,则循环继续,这个过程会不断重复(循环主体,增加步数,然后重新判断)。在条件为假时,循环终止。
当为for(;condition;)时,相当于while(condition)结构
甚至(init;condition;increment)皆不写(认为condition条件为真值),等同于while(1)结构。但无实用价值。
C99允许在for语句的init中定义变量并赋值。如:
for(int i=0; i<10 ; i++)
流程图:
四、循环的嵌套
以上的几个循环语句均可互相嵌套(但要注意语句的完整,以便他人理解)。
五、改变循环执行的状态
有时在某种状态下需要提前结束正在执行的循环。可以利用break和continue语句来实现这一操作。
简单来讲:
break,跳出循环,终止所在的循环;continue,跳过本次循环,进行下次。
它们也遵循就近原则,作用对象为最近的仍在运行的循环。
借用流程图说明:
P137习题
1.画流程图:根据前面的代码,画就行,美观清楚即可。
2.统计循环的次数:只需要在循环主体statement中加入统计的变量,并在最后将其打印出即可。
3.输入两个正整数m和n,求其最大公约数和最小公倍数。
关于最大公约数:
数学上我们常用质因数分解法,短除法,进行求解,但在代码中这种实现困难。我们还要先筛选出质数,不方便。所以我们学习新的方法——辗转相除法。
int main()
{
int m, n;
printf("请以“m n”格式输入:");
scanf("%d %d", &m, &n);
if (m / n == 0)//调换大小顺序,按照辗转相除法
{
int t = m;
m = n;
n = t;
}
while (1)
{
int k = m % n;
if (k != 0)//按照辗转相除法进行等价替换
{
m = n;
n = k;
}
else
{
printf("最大公约数是%d", n);
break;
}
}
return 0;
}
《九章算术》中也有类似的算法,称为更相减损法。也叫等值算法。
int main()
{
int m, n,k,count=0;
printf("请以“m n”格式输入:");
scanf("%d %d", &m, &n);
while (1)
{
if (m % 2 == 0 && n % 2 == 0)//判断是否都是偶数
{
m /= 2, n /= 2;//若是,用2约简
count++;
continue;
}
else;
{
while (1)
{
if (m < n)//比大小,排序
{
int t = m;
m = n;
n = t;
}
k = m - n;//计算差
if (k == m)
{
printf("最大公约数为%d", k * pow(2, count));//乘回约掉的若干2
break; //跳出内循环
}
m = k;
}
}
if (k = m)break;//跳出外循环
}
return 0;
}
当然以上算法还是太过繁琐。而由J. Stein 1961年提出Stein算法,改变了这些。它只有整数的移位和加减法,这对于程序设计者是一个福音。
#include<stdio.h>
int main()
{
int m, n, i;
scanf("%d", &m);
scanf("%d", &n);
for (i = m; i >= 1; i--)
if (m % i == 0 && n % i == 0)
break;
printf("%d\n", i);
return 0;
}
//递归
#include <stdio.h>
#include <math.h>
int gcd(int a, int b) {
if (a % b == 0)
return b;
return gcd(b, a % b);
}
int main(void) {
int a = 10, b = 8;
scanf("%d %d", &a, &b);
printf("GCD: A=>%d, B=>%d (A,B)=%d\n", a, b, gcd(a, b));
return 0;
}
关于最小公倍数:
数学上常用分解质因数法,但它需要先用质因数分解法将全部公有的质因数和独有的质因数提取出来,太过繁琐。
好在有最大公因数和最小公倍数之间的性质:两个自然数的乘积等于这两个自然数的最大公约数和最小公倍数的乘积。
我们就只需要在求解最大公因数后加上一串代码即可。
在辗转相除法后加:
#include<stdio.h>
int gcd(int a, int b);
int lcm(int a, int b);
int main(void)
{
int m, n, result_gcd, result_lcm;
printf("求两个数的最大公约数及最小公倍数?\n请输入你想计算的两个数:\n");
scanf("%d%d", &m, &n);
result_gcd = gcd(m, n);
result_lcm = lcm(m, n);
printf("最大公约数为:%d\n最小公倍数为:%d\n", result_gcd, result_lcm);
return 0;
}
int gcd(int a, int b)
{
int temp;
if (a < b)
{
//交换两个数,使大数放在a上
temp = a;
a = b;
b = temp;
}
while (b != 0)
{
//利用辗除法,直到b为0为止
temp = a % b;
a = b;
b = temp;
}
return a;
}
int lcm(int a, int b)
{
int temp_lcm;
temp_lcm = a * b / gcd(a, b);//最小公倍数等于两数之积除以其最大公约数
return temp_lcm;
}
在Stein算法后加:
#include <stdio.h>
#include <math.h>
int gcd(int a, int b) {
if (a % b == 0)
return b;
return gcd(b, a % b);
}
int lcm(int a, int b)
{
int temp_lcm;
temp_lcm = a * b / gcd(a, b);//最小公倍数等于两数之积除以其最大公约数
return temp_lcm;
}
int main(void) {
int a , b ;
printf("请以“A B”格式输入:");
scanf("%d %d", &a, &b);
printf("GCD:(A,B)=%d\n", a, b, gcd(a, b));
printf("LCM:[A,B]=%d\n", lcm(a, b));
return 0;
}
4.输入一行字符,分别统计出其英文字母、空格、数字、和其他字符的个数。
int main()
{
char asc;
int a = 0, b = 0, c = 0, d = 0;
printf("请输入一串字符:");
while ((asc=getchar())!= '\n')
{
if (asc >= 'a' && asc <= 'z' || asc >= 'A' && asc <= 'Z')
a++;
else if (asc == ' ')
b++;
else if (asc >= '0' && asc <= '9')
c++;
else
d++;
}
printf("字母数:%d\n空格数:%d\n数字数:%d\n其他字符数:%d", a,b,c,d);
return 0;
}
5.求Sn=a+aa+aaa+...+aa...aa(n个a)之值,其中a是一个数字,n表示的位数,n由键盘输入。例如:2+22+222+2222+22222(此时n=5)
#include<math.h>
int main()
{
int a, n, Sn=0,b=0;
printf("请输入a的值:");
scanf("%d", &a);
printf("请输入n的值:");
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
b+= a * pow(10, i);
Sn += b;
}
printf("Sn的值为%d", Sn);
return 0;
}
另一种写法:
#include<math.h>
int main()
{
int a, n,Sn=0;
printf("请输入a的值:");
scanf("%d", &a);
printf("请输入n的值:");
scanf("%d", &n);
int b=a;//起保护作用
for (int i = 0; i < n; i++)
{
Sn += a;//巧妙的顺序使代码简洁
a = a * 10+b;//可将数分解为aa=a*10+a
}
printf("Sn的值为%d", Sn);
return 0;
}
6.求 n! (即求1!+2!+3!+4!+。。。+20!)
int main()
{
double n=1, sum=0;//不使用(long)int,超过数据范围
for (int i = 1; i <= 20; i++)
{
n *= i;
sum +=n;
}
printf("用科学计数法表示,值为%22.15e\n", sum);//提高精度,科学计数法表示
return 0;
}
7.求。
int main()
{
double sum1 = 0, sum2 = 0, sum3 = 0;
for (int i = 1; i <= 100; i++)
{
sum1 += i;
}
for (int i = 1; i <= 50; i++)
{
sum2 += pow(i,2);;
}
for (int i = 1; i <= 10; i++)
{
sum3 =sum3+ 1/(static_cast<double>(i));//VS编译器要求
}
printf("值为%10.8f\n", sum1+sum2+sum3);
return 0;
}
8.输出所有的”水仙花数”,所谓“水仙花数”是指一个3位数,其各位数字立方和等于该数本身。例如,153是水仙花数,因为。
int main()
{
int num=100, a, b, c;
printf("水仙花数为");
for (;num > 99 && num < 1000;num++)
{
a = num / 100 % 10;
b = num / 10 % 10;
c = num % 10;
if (pow(a, 3) + pow(b, 3) + pow(c, 3) == num)
//if(a*a*a+b*b*b+c*c*c==num)
printf("%d ", num);
}
return 0;
}
9.一个数如果恰好等于它的因子之和,这个数就称为"完数"。例如,6的因子为1,2,3,而6=1+2+3,因此6是“完数”。编译程序找出1000之内的所有完数,并按下面的格式输出其因子:
6 its factors are 1,2,3
int main()
{
int sum,i;
for (int j = 2; j < 1000; j++)
{
sum = 0;
for ( i = 1; i < j; i++)
{
if (j % i == 0)
sum += i;
}
if (sum == j)//找出完数
{
printf("%d its factors are ", sum);
for (i = 1; i < j; i++)
{
if (j % i == 0)//打印其因子
printf("%d ", i);
}
printf("\n");
}
}
return 0;
}
10.有一个分数序列,……求出这个数列的前20项和。
int main()
{
double m = 2.0, n = 1.0,sum=0,k;
for(int i=0; i < 20;i++)
{
sum += m/n;
k = m;
m = m + n;
n = k;
}
printf("sum=%16.10f", sum);
return 0;
}
11.一个球从100m高度自由落下,每次落地后反弹回原高度的一半,再落下,再反弹。求它在第十次落地的时共经过多少米,第十次反弹多高。
int main()
{
int n;
double sum=100.0,m=sum/2;
for (n=2; n<=10; n++)
{
sum += 2*m;
m /= 2;
}
printf("第10次反弹%8.6fm\n", m);
printf("在第10次落体时共经过了%8.6fm", sum);
return 0;
}
12.猴子吃桃问题。猴子第1天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。第2天早上又将剩下的桃子吃掉一半,又多吃一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上再想吃时,就只剩一个桃子了。求第1天共摘多少个桃子。
显然反向计算较为方便:
int main()
{
int sum = 1,n;
for (n = 9; n >= 1; n--)
{
sum = 2 * (sum + 1);
}
printf("一共为%d个", sum);
return 0;
}
13.用迭代法求。求平方根的迭代公式为。要求前后两次求出的x的差的绝对值小于。
int main()
{
float a, x0, x1;
printf("请输入需要开根的数:");
scanf("%f", &a);
x0 = a / 2;//设定一个初值(任意)
x1 = (x0 + a / x0) / 2;
do
{
x0 = x1;
x1 = (x0 + a / x0) / 2;
} while (fabs(x0 - x1) >= 1e-5);//判断误差
printf("经过迭代法计算的结果是%8.5f", x1);
return 0;
}
14.用牛顿迭代法求下面方程在1.5附近的根:
int main()
{
double x1, x0, f, f1;
x1 = 1.5;
do
{
x0 = x1;
f = ((2 * x0 - 4) * x0 + 3) * x0 - 6;//原函数
f1 = (6 * x0 - 8) * x0 + 3;//一阶导
x1 = x0 - f / f1;
} while (fabs(x1 - x0) >= 1e-5);//判断
printf("在1.5附近的根为%5.2f", x1);
return 0;
}
15.用二分法求下面方程在(-10,10)的根:
int main()
{
float x0, x1, x2, fx0, fx1, fx2;
do
{
printf("enter x1 & x2:");
scanf("%f,%f", &x1, &x2);
fx1 = x1 * ((2 * x1 - 4) * x1 + 3) - 6;
fx2 = x2 * ((2 * x2 - 4) * x2 + 3) - 6;
} while (fx1 * fx2 > 0);
do
{
x0 = (x1 + x2) / 2;
fx0 = x0 * ((2 * x0 - 4) * x0 + 3) - 6;
if ((fx0 * fx1) < 0)
{
x2 = x0;
fx2 = fx0;
}
else
{
x1 = x0;
fx1 = fx0;
}
} while (fabs(fx0) >= 1e-5);
printf("x=%6.2f\n", x0);
return 0;
}
16.输出以下图案:
*
***
*****
*******
*****
***
*
#include <stdio.h>
int main()
{
int i, j, k;
for (i = 0; i <= 3; i++)
{
for (j = 0; j <= 2 - i; j++)
printf(" ");
for (k = 0; k <= 2 * i; k++)
printf("*");
printf("\n");
}
for (i = 0; i <= 2; i++)
{
for (j = 0; j <= i; j++)
printf(" ");
for (k = 0; k <= 4 - 2 * i; k++)
printf("*");
printf("\n");
}
return 0;
}
17.两个乒乓球队进行比赛,各出3人。甲队为A,B,C3人,乙队为X,Y,Z3人。已抽签决定比赛名单。有人向队员打听比赛的名单,A说他不和 X 比,C说他不和 X,Z比,请编程序找出3对赛手的名单。
int main()
{
char i, j, k; // i是a的对手;j是b的对手;k是c的对手
for (i = 'x'; i <= 'z'; i++)
for (j = 'x'; j <= 'z'; j++)
if (i != j)
for (k = 'x'; k <= 'z'; k++)
if (i != k && j != k)
if (i != 'x' && k != 'x' && k != 'z')
printf("A--%c\nB--%c\nC--%c\n", i, j, k);
return 0;
}