《C語言程式設計》(譚浩強第五版) 第5章 迴圈結構程式設計 習題解析與答案
你也可以上程式咖(https://meta.chengxuka.com),開啟大學幕題板塊,不但有答案,講解,還可以線上答題。
題目1:請畫出例 5.6 中給出的3個程式段的流程圖。
解∶下面分別是教材第5章例5.6給出的程式,據此畫出流程圖。
(1)程式1:
#include <stdio.h> int main() { int i, j, n = 0; for (i = 1; i <= 4; i++) // n用來累計輸出資料的個數 for (j = 1; j <= 5; j++, n++) { if (n % 5 == 0) printf("\n"); //控制在輸出5個數據後換行 printf("%d\t", i * j); } printf("\n"); return 0; }
執行結果:
其對應的流程圖見圖5. 1。
(2)程式2:
#include <stdio.h> int main() { int i, j, n = 0; for (i = 1; i <= 4; i++) for (j = 1; j <= 5; j++, n++) { if (n % 5 == 0) printf("\n"); //控制在輸出5個數據後換行 if (i == 3 && j == 1) break; //遇到第3行第1列,結束內迴圈 printf("%d\t", i * j); } printf("\n"); return 0; }
執行結果:
遇到第3行第1列時,執行 break,結束內迴圈,進行第 4 次外迴圈。
其對應的流程圖見圖 5.2 。
(3)程式 3:
#include <stdio.h> int main() { int i, j, n = 0; for (i = 1; i <= 4; i++) for (j = 1; j <= 5; j++, n++) { if (n % 5 == 0) printf("\n"); //控制在輸出5個數據後換行 if (i == 3 && j == 1) continue; //遇到第3行第1列,終止本次內迴圈 printf("%d\t", i * j); } printf("\n"); return 0; }
執行結果:
遇到第3行第1列時,執行continue,只是提前結束本次內迴圈,不輸出原來的第3行第1列的數3,而進行下一次內迴圈,接著在該位置上輸出原來的第 3行第 2列的數6。
請仔細區分 break 語句和 continue 語句。
其對應的流程圖見圖 5.3。
題目2:請補充例 5.7 程式,分別統計當" fabs(t)>=1e-6"和"fabs(t)>=1e-8" 時執行迴圈體的次數。
解:
例5.7 程式是用
$$
\frac{\pi}{4}\approx 1-\frac{1}{3}+\frac{1}{5}-\frac{1}{7}+...
$$
公式求 π 的近似值,直到發現某一項的絕對值小於 10-6 為止。根據本題要求,分別統計當 fabs(t)>=1e-6 和 fabs(t)>=1e-8 時,執行迴圈體的次數。
(1)採用fabs(t)>=le-6作為迴圈終止條件的程式補充修改如下∶
#include <stdio.h>
#include <math.h> //程式中用到數學函式 fabs,應包含標頭檔案math.h
int main()
{
int sign = 1, count = 0; // sign 用來表示數值的符號,count 用來累計迴圈次數
double pi = 0.0, n = 1.0, term = 1.0; // pi開始代表多項式的值,最後代表π的值,,n代表分母,
// term 代表當前項的值
while (fabs(term) >= 1e-6) //檢查當前項 term 的絕對值是否大於或等於10的(-6)次方
{
pi = pi + term; //把當前項 term 累加到 pi中
n = n + 2; // n+2是下一項的分母
sign = -sign; // sign代表符號,下一項的符號與上一項符號相反
term = sign / n; //求出下一項的值 term
count++; // count 累加1
}
pi = pi * 4; //多項式的和pi乘以4,才是π的近似值
printf("pi=%10.8f\n", pi); //輸出π的近似值
printf("count=%d\n", count); //輸出count的值
return 0;
}
執行結果:
執行50萬次迴圈。
(2) 採用fabs(t)>= 1e-8作為迴圈終止條件的程式,只需把上面程式的第8行如下修改即可:
while (fabs(term) >= 1e-8)
執行結果:
執行5000萬次迴圈。
題目3:輸入兩個正整數 m 和 n,求其最大公約數和最小公倍數。
解:
答案程式碼:
#include <stdio.h>
int main()
{
int p, r, n, m, temp;
printf("請輸入兩個正整數n.m∶");
scanf("%d,%d,", &n, &m);
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:輸入一行字元,分別統計出其中英文字母、空格、數字和其他字元的個數。
解:
答案程式碼:
#include <stdio.h>
int main()
{
char c;
int letters = 0, space = 0, digit = 0, other = 0;
printf("請輸人一行字元:\n");
while ((c = getchar()) != '\n')
if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
letters++;
else if (c == ' ')
space++;
else if (c >= '0' && c <= '9')
digit++;
else
other++;
printf("字母數:%d\n空格數:%d\n數字數:%d\n其他字元數:%d\n", letters, space, digit, other);
return 0;
}
執行結果:
題目5:求 $ S_n=a+aa+aaa+\dots+\overbrace{aa\dots a}^{\text{n個a}} $ 之值,其中a是一個數字,n表示a的位數,n由鍵盤輸入。例如:2+22+222+2222+22222 (此時 n=5)
解:
答案程式碼:
#include <stdio.h>
int main()
{
int a, n, i = 1, sn = 0, tn = 0;
printf("a,n=:");
scanf("%d, %d", &a, &n);
while (i <= n)
{
tn = tn + a; //賦值後的tn為i個a組成數的值
sn = sn + tn; //賦值後的 sn為多項式前i項之和
a = a * 10;
++i;
}
printf("a十aa十aa十...=%d\n", sn);
return 0;
}
執行結果:
題目6:求 $ \sum_{n=1}^{20}{n!} $ (即求1!+2!+3!+4!+…+20!)。
解:
答案程式碼:
#include <stdio.h>
int main()
{
double s = 0, t = 1;
int n;
for (n = 1; n <= 20; n++)
{
t = t * n;
s = s + t;
}
printf("1!+2!+...+20!=%22.15e\n", s);
return 0;
}
執行結果:
請注意:s 不應定義為 int 型或 long 型,因為在用 Turbo C 或 Turbo C++ 等編譯系統時,int 型資料在記憶體佔 2個位元組,整數的範圍為-32768~32767,long 資料在記憶體佔 4 個位元組,整數的範圍為 -21億~21億。用Visual C++ 6.0 時,int 型和 long 型資料在記憶體都佔4 個位元組,資料的範圍為-21億~21 億。無法容納求得的結果。今將 s 定義為 double 型,以得到更多的精度。在輸出時,用 22.15e 格式,使資料寬度為 22,數字部分中小數位數為15位。
題目7:求 $ \sum_{k=1}{100}k+\sum_{k=1}{50}k2+\sum_{k=1}{10}\frac{1}{k} $ 。
解:
答案程式碼:
#include <stdio.h>
int main()
{
int nl = 100, n2 = 50, n3 = 10;
double k, s1 = 0, s2 = 0, s3 = 0;
for (k = 1; k <= nl; k++) //計算1~100的和
{
s1 = s1 + k;
}
for (k = 1; k <= n2; k++) //計算1~50各數的平方和
{
s2 = s2 + k * k;
}
for (k = 1; k <= n3; k++) //計算 1~10 的各倒數和
{
s3 = s3 + 1 / k;
}
printf("sum=%15.6f\n", s1 + s2 + s3);
return 0;
}
執行結果∶
題目8:輸出所有的"水仙花數",所謂"水仙花數"是指—個 3位數,其各位數字立方和等於該數本身。例如,153是水仙花數,因為 $ 153=13+53+3^3 $ 。
解:
答案程式碼:
#include <stdio.h>
int main()
{
int i, j, k, n;
printf("parcissus numbers are ");
for (n = 100; n < 1000; n++)
{
i = n / 100;
j = n / 10 - i * 10;
k = n % 10;
if (n == i * i * i + j * j * j + k * k * k)
printf("%d ", n);
}
printf("\n");
return 0;
}
執行結果:
題目9:一個數如果恰好等於它的因子之和,這個數就稱為"完數"。例如,6的因子為1,2,3,而 6=1+2+3 ,因此 6 是"完數"。程式設計序找出 1000 之內的所有完數,並按下面格式輸出其因子:
6 its factors are 1,2,3
解:方法一。
答案程式碼:
#include <stdio.h>
#define M 1000 //定義尋找範圍
int main()
{
int k1, k2, k3, k4, k5, k6, k7, k8, k9, k10;
int i, a, n, s;
for (a = 2; a <= M; a++) // a是2~1000的整數,檢查它是否完數
{
n = 0; // n 用來累計 a的因子的個數
s = a; // s用來存放尚未求出的因子之和,開始時等於a
for (i = 1; i < a; i++) //檢查i是否 a的因子
if (a % i == 0) //如果i是 a的因子
{
n++; // n加1,表示新找到一個因子
s = s - i; // s減去已找到的因子,s的新值是尚未求出的因子之和
switch (n) //將找到的因子賦給k1~k9,或 k10
{
case 1:
k1 = i; //找出的第1個因子賦給 k1
break;
case 2:
k2 = i; //找出的第2個因子賦給 k2
break;
case 3:
k3 = i; //找出的第3個因子賦給 k3
break;
case 4:
k4 = i; //找出的第 4個因子賦給k4
break;
case 5:
k5 = i; //找出的第5個因子賦給 k5
break;
case 6:
k6 = i; //找出的第6個因子賦給 k6
break;
case 7:
k7 = i; //找出的第7個因子賦給 k7
break;
case 8:
k8 = i; //找出的第 8個因子賦給 k8
break;
case 9:
k9 = i; //找出的第9個因子賦給 k9
break;
case 10:
k10 = i; //找出的第 10個因子賦給k10
break;
}
}
if (s == 0)
{
printf("%d ,Its factors are", a);
if (n > 1)
printf("%d,%d", k1, k2); // n > 1表示a至少有2個因子
if (n > 2)
printf(",%d", k3); // n>2表示至少有3個因子,故應再輸出一個因子
if (n > 3)
printf(",%d", k4); // n>3表示至少有4個因子,故應再輸出一個因子
if (n > 4)
printf(",%d", k5); //以下類似
if (n > 5)
printf(",%d", k6);
if (n > 6)
printf(",%d", k7);
if (n > 7)
printf(",%d", k8);
if (n > 8)
printf(",%d", k9);
if (n > 9)
printf(",%d", k10);
printf("\n");
}
}
return 0;
}
執行結果:
方法二。
答案程式碼:
#include <stdio.h>
int main()
{
int m, s, i;
for (m = 2; m < 1000; m++)
{
s = 0;
for (i = 1; i < m; i++)
if ((m % i) == 0)
s = s + i;
if (s == m)
{
printf("%d,its factors are", m);
for (i = 1; i < m; i++)
if (m % i == 0)
printf("%d ", i);
printf("\n");
}
}
return 0;
}
執行結果:
題目10:有一個分數序列
$$
\frac{2}{1},\frac{3}{2},\frac{5}{3},\frac{8}{5},\frac{13}{8},\frac{21}{13}...
$$
求出這個數列的前20項之和。
解∶
答案程式碼:
#include <stdio.h>
int main()
{
int i, n = 20;
double a = 2, b = 1, s = 0, t;
for (i = 1; i <= n; i++)
{
s = s + a / b;
t = a, a = a + b, b = t;
}
printf("sum=%16.10f\n", s);
return 0;
}
執行結果∶
題目11:一個球從100m高度自由落下,每次落地後反彈回原高度的一半,再落下,再反彈。求它在第 10 次落地時共經過多少米,第 10次反彈多高。
解∶
答案程式碼;
#include <stdio.h>
int main()
{
double sn = 100, hn = sn / 2;
int n;
for (n = 2; n <= 10; n++)
{
sn = sn + 2 * hn; //第 n次落地時共經過的米數
hn = hn / 2; //第n次反跳高度
}
printf("第10次落地時共經過%f米\n", sn);
printf("第10次反彈%f米\n", hn);
return 0;
}
執行結果∶
題目12:猴子吃桃問題。猴子第1天摘下若干個桃子,當即吃了一半,還不過癮,又多吃了一個。第 2天早上又將剩下的桃子吃掉一半,又多吃了一個。以後每天早上都吃了前一天剩下的一半零一個。到第10天早上想再吃時,就只剩一個桃子了。求第1天共摘多少個桃子。
解:
答案程式碼:
#include <stdio.h>
int main()
{
int day, x1, x2;
day = 9;
x2 = 1;
while (day > 0) //第1天的桃子數是第2天桃子數加1後的2倍
{
x1 = (x2 + 1) * 2;
x2 = x1;
day--;
}
printf("total=%d\n", x1);
return 0;
}
執行結果∶
題目13:用迭代法求 $ x=\sqrt{a} $ 。求平方根的迭代公式為
$$
x_{n+1} = \frac{1}{2}(x_n)+\frac{a}{x_n}
$$
要求前後兩次求出的 $ x $ 的差的絕對值小於 $ 10^{-5} $ 。
解:
用迭代法求平方根的演算法如下∶
(1)設定一個 $ x $ 的初值 $ x_0 $ ;
(2)用以上公式求出 $ x $ 的下一個值 $ x_1 $ ;
(3)再將 $ x_1 $ 代入以上公式右側的 $ x_n $ ,求出 $ x $ 的下一個值 $ x_2 $ ;
(4)如此繼續下去,直到前後兩次求出的 $ x $ 值( $ x $ 和 $ x_n+1 $ )滿足以下關係:
$$
|x_{n+1} - x_n | \lt 10^{-5}
$$
為了便於程式處理,今只用 $ x_0 $ 和 $ x_1 $ ,先令 $ x $ 的初值 $ x_0=a/2 $ (也可以是另外的值),求出 $ x_1 $ ;如果此時 $ |x_1 - x_0| \ge 10^{-5} $ 就使 $ x_1 \Rightarrow x_0 $ ,然後用這個新的 $ x_0 $ 求出下一個 $ x_1 $ ;如此反覆,直到 $ |x_1-x_0| \lt 10^{-5} $ 為止。
答案程式碼:
#include <stdio.h>
#include <math.h>
int main()
{
float a, x0, x1;
printf("enter a positive number:");
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("The square root of %5.2f is %8.5f\n", a, x1);
return 0;
}
執行結果∶
題目14:用牛頓迭代法求下面方程在1.5附近的根:
$$
2x3-4x2+3x-6=0
$$
解:
牛頓迭代法又稱牛頓切線法,它採用以下的方法求根:先任意設定一個與真實的根接近的值 $ x_0 $ 。作為第 1 次近似根,由 $ x_0 $ 求出 $ f(x_0) $ ,過 $ (x_0, f(x_0)) $ 點做 $ f(x) $ 的切線,交 $ x $ 軸於 $ x_1 $ ,把 $ x_1 $ 作為第 2 次近似根,再由 $ x_1 $ 求出 $ f(x_1) $ ,過 $ (x_1, f(x_1)) $ 點做 $ f(x) $ 的切線,交 $ x $ 軸於 $ x_2 $ ,再求出 $ f(x_2) $ ,再作切線……如此繼續下去,直到足夠接近真正的根 $ x^* $ 為止,見圖5.4。
從圖5.4可以看出:
$$
f'(x_0)=\frac{f(x_0)}{x_1-x_0}
$$
因此
$$
x_1=x_0-\frac{f(x_0)}{f'(x_0)}
$$
這就是牛頓迭代公式。可以利用它由 $ x_0 $ 求出 $ x_1 $ ,然後由 $ x_1 $ 求出 $ x_2 $ ……
在本題中:
$$
f(x)=2x3-4x2+3x-6
$$
可以寫成以下形式:
$$
f(x)=((2x-4)x+3)x-6
$$
同樣,$ f'(x) $ 可寫成:
$$
f'(x)=6x^2-8x+3=(6x-8)x+3
$$
用這種方法表示的表示式在運算時可節省時間。例如,求 $ f(x) $ 只需要進行 3 次乘法和 3 次加法,而原來的表示式要經過多次指數運算、對數運算和乘法、加法運算,花費時間較多。
但是由於計算機的運算速度越來越快,這點時間開銷是微不足道的。這是以前計算機的運算速度較慢時所提出的問題。由於過去編寫的程式往往採用了這種形式,所以在此也順便介紹一下,以便在閱讀別人所寫的程式時知其所以然。
答案程式碼:
#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("The root of equation is %5.2f\n", x1);
return 0;
}
執行結果∶
為了便於迴圈處理,程式中只設了變數 x0 和 x1,x0 代表前一次的近似根,x1代表後一次的近似根。在求出一個x1 後,把它的值賦給x0,然後用它求下一個x1。由於第1次執行迴圈體時,需要對 x0 賦值,故在開始時應先對 x1 賦一個初值(今為1.5,也可以是接近真實根的其他值)。
題目15:用二分法求下面方程在(-10,10)的根:
$$
2x3-4x2+3x-6=0
$$
解:
二分法的思路為∶先指定一個區間 $ [x_1,x_2] $ ,如果函式 $ f(x) $ 在此區間是單調變化,可以根據 $ f(x_1) $ 和 $ f(x_2) $ 是否同符號來確定方程 $ f(x)=0 $ 在 $ [x_1,x_2] $ 區間是否有一個實根。若 $ f(x_1) $ 和 $ f(x_2) $ 不同符號,則 $ f(x)=0 $ 在 $ [x_1,x_2] $ 區間必有一個(且只有一個)實根; 如果 $ f(x_1) $ 和 $ f(x_2) $ 同符號,說明在$ [x_1,x_2] $ 區間無實根,要重新改變 $ x_1 $ 和 $ x_2 $ 的值。當確定 $ [x_1,x_2] $ 有一個實根後,採取二分法將 $ [x_1,x_2] $ 區間一分為二,再判斷在哪一個小區間中有實根。 如此不斷進行下去,直到小區間足夠小為止,見圖5.5。
演算法如下:
(1)輸入 $ x_1 $ 和 $ x_2 $ 的值。
(2)求出 $ f(x_1) $ 和 $ f(x_2) $ 。
(3)如果 $ f(x_1) $ 和 $ f(x_2) $ 同符號,說明在 $ [x_1,x_2] $ 區間無實根,返回(1),重新輸入 $ x_1 $ 和 $ x_2 $ 的值; 若 $ f(x_1) $ 和 $ f(x_2) $ 不同符號,則在 $ [x_1,x_2] $ 區間必有一個實根,執行(4)。
(4)求 $ x_1 $ 和 $ x_2 $ 間的中點:$ x_0=\frac{x_1+x_2}{2} $ 。
(5)求出 $ f(x_0) $ 。
(6)判斷 $ f(x_0) $ 和 $ f(x_1) $ 是否同符號。
①如同符號,則應在 $ [x_0,x_2] $ 中去找根,此 時 $ x_1 $ 已 不起作用,用 $ x_0 $ 代替 $ x_1 $,用 $ f(x_0) $ 代替 $ f(x_1) $ 。
②如用 $ f(x_0) $ 與 $ f(x_1) $ 不同符號,說明應在 $ [x_1,x_0] $ 中去找根,此時 $ x_2 $ 已不起作用,用 $ x_0 $ 代替 $ x_2 $ ,用 $ f(x_0) $ 代替 $ f(x_2) $ 。
(7)判斷 $ f(x_0) $ 的絕對值是否小於某一個指定的值(例如 $ 10^{-5}$ )。若不小於 $ 10^{-5}$ ,就返回(4),重複執行(4)、(5)、(6);若小於 $ 10^{-5}$ ,則執行(8)。
(8)輸出 $ x_0 $ 的值,它就是所求出的近似根。
N-S圖見圖5.6。
答案程式碼:
#include <stdio.h>
#include <math.h>
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對賽手的名單。
解:
先分析題目。按題意,畫出圖5.7的示意圖。
圖5.7中帶 $ \times $ 符號的虛線表示不允許的組合。從圖中可以看到∶①X既不與 A比賽,又不與C比賽,必然與B比賽。②C既不與X比賽,又不與Z比賽,必然與Y比賽。③剩下的只能是A與Z比賽,見圖5.8。
以上是經過邏輯推理得到的結論。用計算機程式處理此問題時,不可能立即就得出結論,而必須對每一種成對的組合一一檢驗,看它們是否符合條件。 開始時,並不知道A,B,C與X,Y,Z中哪一個比賽,可以假設∶A與i比賽,B與j比賽,C與k 比賽,即∶
A—i,
B—j,
C—k
i,j,k分別是X,Y,Z之一,且i,j,k 互不相等(一個隊員不能與對方的兩人比賽),見圖5.9。
外迴圈使 i 由 'X' 變到 'Z' ,中迴圈使 j 由 'X' 變到 'Z'(但 i 不應與 j 相等)。然後對每一組 i、j 的值,找符合條件的k 值。k 同樣也可能是 'X'、'Y'、'Z' 之一,但 k 也不應與 i 或 j 相等。在 i≠j≠k 的條件下,再把 i≠'X' 和 k≠'X' 以及k≠'Z' 的 i,j,k的值輸出即可。
答案程式碼:
#include <stdio.h>
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;
}
執行結果∶
說明:
(1)整個執行部分只有一個語句,所以只在語句的最後有一個分號。請讀者弄清楚迴圈和選擇結構的巢狀關係。
(2)分析最下面一個if語句中的條件;i≠'X',k≠'X',k≠'Z',因為已事先假定 A—i,B—j,C—k,由於題目規定 A不與X對抗,因此i不能等於'X',同理,C不與X,Z對抗,因此k 不應等於'X'和'Z'。
(3)題目給的是 A,B,C,X,Y,Z,而程式中用了加撇號的字元常量'X','Y','Z',這是為什麼?這是為了在執行時能直接輸出字元A,B,C,X,Y,Z,以表示 3組對抗的情況。