第五章
1.请画出例5.6中给出的3个程序段的流程图
流程图1:
流程图2:
流程图3:
2.请补充例5. 7程序,分别统计当“fabs(t)>= le- 6”和“fabs(t)> = le- 8”时执行循环体的次数。
fabs(t)>= le- 6 ,示例代码
# include <stdio.h>
# include <math.h>
int main()
{
int sign = 1;
double pi = 0.0, term = 1.0;
int n = 0;
while (fabs(term) >= 1e-6)
{
n++;
term = 1.0 / (2 * n - 1)*sign;
pi += term;
sign = -sign;
}
pi *= 4;
printf("pi的近似值是%lf\n", pi);
printf("循环体循环了%d次\n", n);
return 0;
}
运行截图:
fabs(t)> = le- 8,示例代码
# include <stdio.h>
# include <math.h>
int main()
{
int sign = 1;
double pi = 0.0, term = 1.0;
int n = 0;
while (fabs(term) >= 1e-8)
{
n++;
term = 1.0 / (2 * n - 1)*sign;
pi += term;
sign = -sign;
}
pi *= 4;
printf("pi的近似值是%lf\n", pi);
printf("循环体循环了%d次\n", n);
return 0;
}
运行截图:
3.输人两个正整数m和n,求其最大公约数和最小公倍数
答案解析:
该题题目直接使用“辗转相除法”来求解最大公约数,以除数和余数反复做除法运算,当余数为 0 时,就取得当前算式除数为最大公约数。
最大公约数和最小公倍数之间的性质:两个自然数的乘积等于这两个自然数的最大公约数和最小公倍数的乘积。所以,当我们求出最大公约数,就可以很轻松的求出最小公倍数。
代码示例:
#include <stdio.h>
int main()
{
int p, r, n, m, temp;
printf("请输入两个正整数n,m:");
scanf("%d%d,", &n, &m);
//调整n保存较大的值
if (n < m)
{
temp = n;
n = m;
m = temp;
}
p = n * m;
while (m != 0)
{
r = n % m;
n = m;
m = r;
}
printf("它们的最大公约数为:%d\n", n);
printf("它们的最小公倍数为:%d\n", p / n);
return 0;
}
运行截图:
4.输人一行字符,分别统计出其中英文字母、空格、数字和其他字符的个数。
答案解析:
该题可以调用getchar
函数,从stdin流中读入一个字符,当输入多个字符时,getchar()再执行时就会直接从缓冲区中读取了。等同于getc(stdin)
。所以,我们循环调用getchar,直到将标准输入的内容读到换行符\n
为止。同时判断,读取到的字符是英文字母、空格、数字或者其他字符,并计数;
代码示例:
#include <stdio.h>
int main()
{
char c;
//定义eng_char为英文字母的个数,初始值为0
//定义space_char为空格字符的个数,初始值为0
//定义digit_char为数字字符的个数,初始值为0
//定义other_char为其他字符的个数,初始值为0
int eng_char = 0, space_char = 0, digit_char = 0, other_char = 0;
printf("请输入一行字符:");
while ((c = getchar()) != \'\n\')
{
if (c >= \'a\' && c <= \'z\' || c >= \'A\' && c <= \'Z\')
{
eng_char++;
}
else if (c == \' \')
{
space_char++;
}
else if (c >= \'0\' && c <= \'9\')
{
digit_char++;
}
else
{
other_char++;
}
}
printf("英文字母数量:%d\n空格数量:%d\n数字数量:%d\n其他字符数量:%d\n", eng_char, space_char, digit_char, other_char);
return 0;
}
运行截图:
5.求 S n S_n Sn=a+aa+aaa+…+ a a + ⋯ + a ⏞ n 个 a \overbrace{aa+\dots+a}^{n个a} aa+⋯+a n个a 之值,其中a是一个数字,n表示a的位数,n由键盘输入。例如:
2+22+222+2222+22222 (此时n=5)
答案解析:
该题目可以将数字拆分为 a * 10^n + 前一个数字
,例如:
2 = 2 * 10^0 + 0
: 默认2的前一个数字为0,也就是没有任何值
22 = 2 * 10^1 + 2
: 22的前一个数字为2
222 = 2*10^2 + 22
:222的前一个数字为22
以此类推…
所以,在每次循环的时候,需要保存下,上一次结果的值,方便下一次计算
还需要使用到C库当中使用的pow函数,来计算某个数的n次方,我们在该题目当中使用的是10的n次方,n随着循环的次数,以此递增。
代码示例:
#include <stdio.h>
#include <math.h>
int main()
{
//n为a的个数
int n;
double a, prev_sum = 0.0, total_sum = 0.0;
printf("请输入a的值以及n的值: ");
scanf("%lf %d", &a, &n);
//循环n次求总和
for (int i = 0; i < n; i++)
{
prev_sum += a * pow(10, i);
total_sum += prev_sum;
}
printf("总和为:%lf\n", total_sum);
return 0;
}
运行截图:
6.求 ∑ n = 1 20 n ! \sum\limits_{n=1}^{20}n! n=1∑20n! (即求1!+2!+3!+4!+…+20!)。
答案解析:
该题需要从1循环到20,依次求出每一个数字阶乘的结果。所以在代码当中需要有两个循环,大循环从1到20,保证1到20个数字都被循环到,小循环里计算N阶乘,累加求和。注意:对于20的阶乘已经超出了int
类型能过表示的数字范围,所以在代码当中使用double
类型
代码示例:
#include<stdio.h>
int main()
{
double total_sum = 0;
for(int i = 1; i <= 20; i++)
{
double single_sum = 1;
for (int j = i; j > 0; j--)
{
single_sum *= j;
}
total_sum += single_sum;
}
printf("1~20每个数字阶乘总和为:%lf\n",total_sum);
return 0;
}
运行截图:
7. ∑ k = 1 100 k \sum\limits_{k=1}^{100}k k=1∑100k + ∑ k = 1 50 k 2 \sum\limits_{k=1}^{50}{k}^2 k=1∑50k2 + ∑ k = 1 10 1 k \sum\limits_{k=1}^{10}{\frac{1}{k}} k=1∑10k1 。
答案解析:
对于 ∑ k = 1 100 k \sum\limits_{k=1}^{100}k k=1∑100k而言,指的是求从1到100的和。每个数字为整数,求和也为整数
对于 ∑ k = 1 50 k 2 \sum\limits_{k=1}^{50}{k}^2 k=1∑50k2而言,指的是求从12到502的和。每个数字为整数,求和也为整数。
对于 ∑ k = 1 10 1 k \sum\limits_{k=1}^{10}{\frac{1}{k}} k=1∑10k1而言,指的是求从 1 1 \frac{1}{1} 11到 1 10 \frac{1}{10} 101的和。每个数字不是整数,求和也不是整数。
综上所述:求和结果不是整数,所以定义求和变量是需要定义为带有精度的变量double
该题目,最大的求和是从从1到100,所以需要一个循环,从1遍历到100。针对第一种情况,则遍历100次停下来。针对第二种情况,则遍历50次的时候停下来,针对第三种情况,则遍历10遍就停下来。
最后,在遍历每一个数字的时候,针对三种不同的情况求和。最后将三种不同请求的和加起来就是总体的和
代码示例:
#include <stdio.h>
int main()
{
double total_sum = 0, sum1 = 0, sum2 = 0, sum3 = 0.0;
for (int k = 1; k <= 100; k++)
{
sum1 += k;
//遍历50次就不在执行情况2
if (k <= 50)
{
sum2 += k * k;
}
//遍历10次就不在执行情况3
if (k <= 10)
{
sum3 += 1.0 / k;
}
}
total_sum = sum1 + sum2 + sum3;
printf("三种情况求和结果为:%lf\n", total_sum);
return 0;
}
运行截图:
8.输出所有的“水仙花数”,所谓“水仙花数”是指一个3位数,其各位数字立方和等于该数本身。例如,153是水仙花数,因为153=1*+5*+3。
答案解析:
从题目当中得到”水仙花数“为一个3位数,则范围确定为[100, 999]。另外需要获取该数字的百位数字,十位数字,个位数字相加起来等于该数本身,则我们需要使用到%
除的方式,来获取每一个位权的数字。
代码示例:
#include <stdio.h>
int main()
{
//a表示百位数字,b表示十位数字,c表示各位数字
int a, b, c;
for (int i = 100; i <= 999; i++)
{
a = i / 100;
b = (i / 10) % 10;
c = i % 10;
if (a * a * a + b * b * b + c * c * c == i)
{
printf("%d\n", i);
}
}
return 0;
}
运行截图:
9.一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如,6的因子为1,2,3,而6=1+2+3,因此6是“完数”。编程序找出1000之内的所有完数,并按下面格式输出其因子:
6 its factors are 1,2,3
答案解析:
因子:整数a除以整数b(b≠0) 的商正好是整数而没有余数,我们就说b是a的因子。整数n除以m,结果是无余数的整数,那么我们称m就是n的因子。 需要注意的是,唯有被除数,除数,商皆为整数,余数为零时,此关系才成立。因子是不包括自身的
举一个例子:20 = 4 * 5,则4和5就是20的因子,也被称之为因子
代码示例:
#include<stdio.h>
int main()
{
int data, fator, sum; /* data表示要判断的数,fator表示因子,sum表示因子之和*/
for (data = 2; data <= 1000; data++)
{
//1是所有整数的因子,所以因子之和从1开始
sum = 1;
for (fator = 2; fator <= data / 2; fator++)
{
/* 判断data能否被fator整除,能的话fator即为因子 因子不包括自身 */
if (data % fator == 0)
{
sum += fator;
}
}
// 判断此数是否等于因子之和 */
if (sum == data)
{
printf("%d its factors are 1, ", data);
for (fator = 2; fator <= data / 2; fator++)
{
if (data % fator == 0)
{
printf("%d, ", fator);
}
}
printf("\n");
}
}
return 0;
}
运行截图:
10.有一个分数序列,求出这个数列的前20项之和。
2 1 \frac{2}{1} 12, 3 2 \frac{3}{2} 23, 5 3 \frac{5}{3} 35, 8 5 \frac{8}{5} 58, 13 8 \frac{13}{8} 813, 25 13 \frac{25}{13} 1325,…
答案解析:
从题目当中可以看出来,下一个分式当中的分子为上一个分式中分子和分母的和,分母为上一个分式的分子。通过这个规律不难推出下一个分式的分子和分母,需要注意的是,保存分式的结果不能使用到整数,因为有可能会有小数的存在,所以我们需要选用浮点数double
代码示例:
#include <stdio.h>
//定义循环次数
#define COUNT 20
int main()
{
//定义第一个分式的分子为a, 值为2; 定义分母为b,值为1
//定义相加的和为sum,初始值为0
double a = 2, b = 1, sum = 0;
double temp;
for (int i = 0; i < COUNT; i++)
{
sum += a / b;
//记录前一项分子
temp = a;
//前一项分子与分母之和为后一项分子
a = a + b;
//前一项分子为后一项分母
b = temp;
}
printf("前%d项之和为:sum=%9.7f\n", COUNT, sum);
return 0;
}
运行截图:
11.一个球从100m高度*落下,每次落地后反弹回原高度的一半,再落下,再反弹。求它在第10次落地时共经过多少米,第10次反弹多高。
答案解析:
该题目需要循环10次,在每一循环的时候,需要将下落的高度和回弹的高度加起来。需要注意的点,第10次下落不需要在计算回弹的距离了,所以需要特殊处理下。在计算每次高度的时候,会有小数存在,所以需要选用浮点数
代码示例:
#include <stdio.h>
int main()
{
//总高度
double total_m = 100.0;
//小球经历的米数
double total_sum = 0.0;
for (int i = 0; i < 10; i++)
{
total_sum += total_m;
total_m /= 2;
total_sum += total_m;
}
//不需要计算第10次的反弹高度,所以减去
total_sum -= total_m;
printf("小球总共经历%lf米, 第10次反弹%lf米\n", total_sum, total_m);
return 0;
}
运行截图:
12.猴子吃桃问题。猴子第1天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。第2天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,就只剩一个桃子了。求第1天共摘多少个桃子。
答案解析:
从题面上来看,可以推出,后一天的桃子数量 = 前一天桃子数量 / 2 - 1。所以,该公式也可以写成前一天的桃子数量 = (后一天桃子数量+1) * 2。所以我们知道了第10天剩余桃子的数量,则可以依次推算出桃子的前一天桃子的总数。需要注意的点,猴子只是吃了9天,所以,我们只需要遍历9次就可以了。
代码示例:
#include <stdio.h>
int main()
{
int day = 9;
int prev_day_count;
int cur_day_count = 1;
while (day > 0)
{
prev_day_count = (cur_day_count + 1) * 2;
cur_day_count = prev_day_count;
day--;
}
printf("total count : %d\n", cur_day_count);
return 0;
}
运行截图:
13.用迭代法求x= a \sqrt{a} a。求平方根的迭代公式为
x n + 1 x_{n+1} xn+1 = 1 2 \frac{1}{2} 21( x n x_{n} xn + a x n \frac{a}{x_n} xna)
要求前后两次求出的x的差的绝对值小于 1 0 − 5 10^{-5} 10−5。
答案解析:
题面上已经告诉两条信息,一个是x= a \sqrt{a} a,所以我们可以通过a求出x的值。另外一条是 x n + 1 x_{n+1} xn+1 = 1 2 \frac{1}{2} 21( x n x_{n} xn + a x n \frac{a}{x_n} xna),可以通过x的值求出 x n + 1 x_{n+1} xn+1的值,所以,只需要轮询的计算,不断的计算差值,直到满足差值小于 1 0 − 5 10^{-5} 10−5就可以停止了
代码示例:
#include <stdio.h>
#include <math.h>
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("[%f] 的平方根为 [%f]\n", a, x1);
return 0;
}
运行截图:
14.用牛顿迭代法求下面方程在1.5附近的根:
2 x 3 x^3 x3- 4 x 2 x^2 x2 + 3 x x x - 6= 0
答案解析:
牛顿迭代法的公式为:
x n + 1 x_{n+1} xn+1 = x n x_{n} xn - f ( x n ) f ′ ( x n ) \frac{f(x_{n})}{f\'(x_{n})} f′(xn)f(xn)
其中, x n x_{n} xn为输出的值,在该题目当中为1.5。 f ( x n ) f(x_{n}) f(xn)为公式2 x 3 x^3 x3- 4 x 2 x^2 x2 + 3 x x x - 6。 f ′ ( x n ) f\'(x_{n}) f′(xn)为导数,根据导数原则:
规则1: x n x^n xn = n * x ( n − 1 ) x^{(n-1)} x(n−1), 规则2:常数的导数为0。可以推导出 f ′ ( x n ) f\'(x_{n}) f′(xn) = 6 x 2 x^2 x2 - 8x + 3。
在依照牛顿迭代法计算出 x n + 1 x_{n+1} xn+1的值,直到求出的差值小于0.00001
代码示例:
#include <stdio.h>
#include <math.h>
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附近的根为:%lf\n", x1);
return 0;
}
运行截图:
15.用二分法求下面方程在(-10,10)的根:
2 x 3 x^3 x3- 4 x 2 x^2 x2 + 3 x x x - 6= 0
答案解析:
将区间划分为两部分,记录区间左右端点,得到中点。每次运算将中点带入方程进行运算,求得结果,进行分析:
结果 > 0:将中位数赋值给右端点
结果 < 0:将中位数赋值给左端点
以此类推…
fabs函数是一个求绝对值的函数,求出x的绝对值,和数学上的概念相同;
le-5: 1 0 − 5 10^{-5} 10−5,即0.00001
代码示例:
#include<stdio.h>
#include<math.h>
int main()
{
double left = -10, right = 10, mid;
double temp = 10;
while (fabs(temp) > 1e-5)
{
mid = (left + right) / 2;
//((2x - 4)*x + 3) * x - 6 ==> 2x^3 - 4x^2 + 3x -6
temp = ((2 * mid - 4) * mid + 3) * mid - 6;
if (temp > 0)
{
right = mid;
}
else if (temp < 0)
{
left = mid;
}
}
printf("在(-10,10)的根为:%lf", mid);
return 0;
}
运行截图:
16.输出以下图案:
*
***
*****
*******
*****
***
*
答案解析:
该题目需要关心当前行对应的从最左边到第一颗*
的空格数量以及星星数量。将该题分为两个部分,前面4行和后面3行来进行拆分。
前4行中:
第一行:行号为0, 空格数为3,星星数量为1;
第二行:行号为1, 空格数为2, 星星数量为3;
第三行:行号为2, 空格数为1, 星星数量为5;
第四行:行号为3, 空格数为0,星星数量为7;
则我们可以推出两组关系,即行号和空格数量关系为:空格数 = 3 - 行号 。行号与星星的关系为:星星数 = 2 * 行号 + 1
后三行中:
第一行:行号为0,空格数为1,星星数量为5;
第二行:行号为1, 空格数为2, 星星数量为3;
第三行:行号为2, 空格数为3,星星数量为1;
则我们推出两组关系,即行号与数量的关系:空格数 = 行号 + 1。行号与星星的关系:星星数 = 7 - 2 * (行号+1)
基于上面的关系,我们写出如下代码:
代码示例:
#include <stdio.h>
int main()
{
int cur_row, space_count, start_count;
//输出前4行内容
for (cur_row = 0; cur_row < 4; cur_row++)
{
//计算当前行空格数量,并且进行打印
for (space_count = 3 - cur_row; space_count > 0; space_count--)
{
printf(" ");
}
//计算当前行*数量,并且进行打印
for (start_count = 2 * cur_row + 1; start_count > 0; start_count--)
{
printf("*");
}
printf("\n") ;
}
//输出后三行
for (cur_row = 0; cur_row < 3; cur_row++)
{
for (space_count = cur_row + 1; space_count > 0; space_count--)
{
printf(" ");
}
for (start_count = 7 - 2 * (cur_row + 1); start_count > 0; start_count--)
{
printf("*");
}
printf("\n");
}
return 0;
}
运行截图:
17.两个乒乓球队进行比赛,各出3人。甲队为A,B,C 3人,乙队为X,Y,Z 3人。已抽签决定比赛名单。有人向队员打听比赛的名单,A说他不和X比,C说他不和X,Z比,请编程序找出3对赛手的名单。
答案解析:
从题面上得知,每队为3人,则隐含条件为队内三人是不能比赛的,并且A一定不会和X比,C一定不会X和Z比;则我们不难写出判断条件:
如果A和X比 或者 C和X比 或者 C和Z比 或者 A和B比 或者 A和C比 或者 B和C比,都是不可以的;所以我们只要穷举A比赛对象,B比赛对象,C比赛对象,判断上述条件就可以了;
代码示例:
#include <stdio.h>
int main()
{
int A_battle, B_battle, C_battle;
//如果A对战的对象从“X”到“Z”
for (A_battle = \'X\'; A_battle <= \'Z\'; A_battle++)
{
//如果B对战的对象从“X”到“Z”
for (B_battle = \'X\'; B_battle <= \'Z\'; B_battle++)
{
//如果C对战的对象从“X”到“Z”
for (C_battle = \'X\'; C_battle <= \'Z\'; C_battle++)
{
//去除限制条件
if (A_battle == \'X\' || C_battle == \'X\' || C_battle == \'Z\' || B_battle == A_battle || B_battle == C_battle || A_battle == C_battle)
{
continue;
}
printf("A对%c,B对%c,C对%c", A_battle, B_battle, C_battle);
}
}
}
return 0;
}