




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第三章C程序的流程控制3.4循環(huán)型程序設計
3.4.1迭代與窮舉算法采用循環(huán)(或稱重復)結(jié)構(gòu)是計算機程序的一個重要特征。計算機運算速度快,最宜于用于重復性的工作。在程序設計時,人們也總是把復雜的不易理解的求解過程轉(zhuǎn)換為易于理解的操作的多次重復。這樣,一方面可以降低問題的復雜性,減低程序設計的難度,減少程序書寫及輸入的工作量;另一方面可以充分發(fā)揮計算機運算速度快、能自動執(zhí)行程序的優(yōu)勢。在循環(huán)算法中,窮舉與迭代是兩類具有代表性的基本應用。這一節(jié)的主要內(nèi)容就是引導讀者如何用C語言的三種循環(huán)結(jié)構(gòu)來實現(xiàn)這兩種基本算法。1.迭代迭代是一個不斷用新值取代變量的舊值,或由舊值遞推出變量的新值的過程。例3.12人口增長問題。按年0.2%的增長速度,現(xiàn)有13億人,10年后將有多少人?設現(xiàn)人口數(shù)為m,則第1年后人口變?yōu)椋簃=m*(1+0.2%)即將m的值用m*(1+2%)替代。圖3.18計算人口的N-S圖當i<=10i++m=m*(1+0.2%)初值:m=13,i=1輸出m第2年后,把上述替代(賦值表達式)再執(zhí)行一次。要計算10年后的人口,就是把上述表達式執(zhí)行10次。這也要用循環(huán)結(jié)構(gòu)實現(xiàn),重復操作的內(nèi)容是不斷從一個變量的舊值出發(fā)計算它的新值。圖3.18為計算人口的N-S圖。例3.13兔子繁殖問題。著名意大利數(shù)學家Fibonacci曾提出一個有趣的問題:設有一對新生兔子,從第三個月開始它們每個月都生一對兔子。按此規(guī)律,并假設沒有兔子死亡,一年后共有多少對兔子。人們發(fā)現(xiàn)每月的兔子數(shù)組成如下數(shù)列:1,1,2,3,5,8,13,21,34,…并把它稱為Fibonacci數(shù)列。那么,這個數(shù)列如何導出呢?觀察一下Fibonacci數(shù)列可以發(fā)現(xiàn)這樣一個規(guī)律:從第3個數(shù)開始,每一個數(shù)都是其前面兩個相鄰數(shù)之和。這是因為,在沒有兔子死亡的情況下,每個月的兔子數(shù)由兩部分組成:上一月的老兔子數(shù),這一月剛生下的新兔子數(shù)。上一月的老兔子數(shù)即其前一個數(shù)。這一月剛生下的新兔子數(shù)恰好為上上月的兔子數(shù)。因為上一月的兔子中還有一部分到這個月還不能生小兔子,只有上上月已有的兔子才能每對生一對小兔子。上述算法可以描述為fib1=fib2=1 (1)fibn=fibn-1+fibn-2(n>=3) (2)式(1)為賦初值,式(2)為迭代公式。用C語言來描述式(2)為:fib=fib1+fib2;fib1=fib2;/*為下一次迭代作準備*/fib2=fib;這里,fib1和fib2和不再僅代表第1個月和第2個月的兔子數(shù),而作為中間變量,代表前兩個月的兔子數(shù),fib當前月的兔子數(shù)。圖3.19為Fibonacci算法的N-S圖。表3.2為迭代過程中fib1、fib2和fib的變化情形。圖3.19計算Fiboonacci數(shù)列的N-S圖當i<=12fib1=fib2fib=fib1+fib2初值:fib1=1,fib2=1,i=3fib2=fib輸出fibi++圖3.19計算Fiboonacci數(shù)列的N-S圖
迭代次數(shù)3456789101112fib111235813213455fib2123581321345579fib23581321345579144表3.2按照迭代算法變量fib1、fib2和fib3的變化情形例3.14一元方程的迭代解法。一元方程f(x)=0,反映了科學技術(shù)領(lǐng)域內(nèi)的一類常見的規(guī)律。很多人總習慣于設法構(gòu)造解析表達式去精確地描述一個方程的解。但是,能用解析表達式精確地描述出解的方程實在是太有限了。業(yè)已證明,一元5次以上的方程找不出通用的根的解析表達式。在這種情況下,人們不得不求助于一些近似解法。一種粗略的方法是畫出函數(shù)的曲線,從曲線上估計方程的粗略解。還有一種方法是用迭代法使方程的根不斷地精確化。用迭代法求一元方程f(x)=0的解,就是要把方程f(x)=0,改寫為一種迭代形式:x=φ(x)選取適當?shù)某踔祒0(粗略的解),便可以通過重復迭代構(gòu)造出一個序列:x0,x1,x2,…,xn,…若這個數(shù)列收斂,即存在極限,且函數(shù)連續(xù)(在所求解的區(qū)間),則很容易很到這個極限值就是方程f(x)=0的一個解。(1)二分迭代法用二分迭代法求解一元方程的思想如圖3.20所示。先取f(x)=0的兩個粗略解x1與x2。若f(x1)與f(x2)符號相反,則方程f(x)=0在區(qū)間(x1,x2)中至少有一個根。若f(x)在區(qū)間(x1,x2)內(nèi)單調(diào)(單調(diào)升或單調(diào)降),則(x1,x2)間應有一個實根。取x3=1/2(x1+x2)并在x1與x2中舍去函數(shù)值與f(x3)同號者(圖中為x2,因為f(x2)與f(x3)同符號),x3與剩下的一個粗略解(圖中為x1)組成一個新的小的含根區(qū)間。再取(x1,x3)的中點x4,若f(x1)與f(x4)同符號,則得到更小的含根的區(qū)間(x3,x4)。如此重復,便可以構(gòu)造出一個序列:x1,x2,x3,…,xn-1,xn,…當xn與xn-1之差小于給定的誤差ε0時,xn便是所求的近似解。用C語言描述上述迭代關(guān)系為:root=(x1+x2)/2;x2=root; /*為下一次迭代作準備*/用N-S圖描述上述算法如圖3.21所示。輸出x=(x1+x2)/2當|x1-x2|>=E0f(x)與f(x1)同號x1=xx2=x真假x=(x1+x2)/2輸入兩個端點:x1和x2,誤差E0圖3.21用二分法求一元方程的根該重復過程由誤差控制,而不能用計數(shù)法。因為事先無法估計出所需的迭代次數(shù)。
xkxk+1f(x)x0yxk+2xk+3x*圖3.22牛頓迭代發(fā)圖3.23用牛頓迭代法一元方程的根x2=x1-f(x1)/f′(x2)當|x1-x2|>=E0x1=x2輸入一個猜測解x1,誤差E0x2=x1-f(x1)/f′(x1)輸出x2前面介紹了例3.12、例3.13、例3.14三個迭代的例子。例3.12與例3.13稱為精確迭代。因為這種算法本身(不考慮機器字長所引起的誤差)經(jīng)過有限次迭代可以提供問題的精確解。例3.14稱為近似迭代,也稱逐次逼近法。因為經(jīng)過有限次迭代仍不能得到問題的精確解。但是只要迭代過程收斂,經(jīng)過有限次的迭代,總可以得到足夠精度的近似解。一般說來,精確迭代的迭代次數(shù)是事先可以計算出來的(但不一定用它去控制循環(huán)過程);而近似迭代的迭代次數(shù),事先無法計算出來,它的循環(huán)結(jié)構(gòu)要靠誤差進行控制。注意,在當重復結(jié)構(gòu)之前要先按照迭代公式計算一次x2。否則,沒有辦法計算當循環(huán)的判斷表達式。2.窮舉窮舉是另一種重復型算法。它的基本思想是,對問題的所有可能狀態(tài)一一測試,直到找到解或?qū)⑷靠赡軤顟B(tài)都測試過為止。循環(huán)控制有兩種辦法:計數(shù)法與標志法。計數(shù)法要先確定循環(huán)次數(shù),然后逐次測試,完成測試次數(shù)后,循環(huán)結(jié)束。標志法是達到某一目標后,使循環(huán)結(jié)束。例3.15輸入10個數(shù),將最大的一個數(shù)打印出來。從輸入的10個數(shù)中選取最大的一個數(shù)的算法,可以粗略地看作這樣的過程:、S1:輸入10個數(shù);S2:從10個數(shù)中選最大一個數(shù);S3:輸出最大的一個數(shù)。但是,在介紹數(shù)組之前,目前還沒有合適的方法來存儲10個數(shù)。為此可以考慮類似打擂臺一樣的一種算法:S1:輸入1個數(shù),暫時作為擂主——最大數(shù)。S2:輸入9個數(shù)——窮舉這9個數(shù),每輸入一個,打一次擂臺——把最大數(shù)與剛輸入的數(shù)據(jù)進行比較,把大者作為新擂主——最大數(shù)。S3:輸出最大數(shù)。這個算法可以用圖3.24所示的算法表示為一個重復結(jié)構(gòu)。當i<=9i++n>maxmax=n(空)真假輸入第i個數(shù)n輸入一個數(shù)給max輸出max初始化:i=1圖3.24采用計數(shù)器的10中取大算法
圖3.25采用標志法的多中取大算法
當(n!=FLAG)n>maxmax=n(空)真假輸入下一個數(shù)nmax=n輸出max初始化:i=1,F(xiàn)LAG=-32768輸入一個數(shù)n例3.16百錢買百雞問題。公元前五世紀,我國古代數(shù)學家張丘建在《算經(jīng)》一書中提出了“百雞問題”:雞翁一值錢五,雞母一值錢三,雞雛三值錢一。百錢買百雞,問雞翁、母、雛各幾何?(1)基本解題思路這是一個有名的不定方程問題:cocks+hens+chicks=100(1)5*cocks+3*hens+chicks/3=100(2)式中:cocks:雞翁數(shù)hens:雞母數(shù)chicks:雞雛數(shù)圖3.26百錢買百雞的粗略算法
當(cocks<=19)找滿足題意的hens和chicks輸出一組cocks,hens和chichs初始化:cocks=0cocks++(2)對s2.1細化思路1:把已確定的cocks代入式(1)與(2)中,求解方程,看能否找到滿足題意的解。這種思路不太適合計算機求解。思路2:在每個給定的cocks下,面對hens的取值范圍內(nèi)的各個值依次測試,看能找到哪些hens及chicks滿足題意。于是得到如下s2.1細化算法。
用這段算法代替圖3.26中的“找滿足題意的hens和chicks”,得到圖3.27所示的百錢買百雞的N-S圖算法。s2.1.1:hens=0;s2.1.2:當(hens<=33)s2.1.2.1:找滿足題意的chicks;s2.1.2.2:hens++;/*即hens=hens+1*/(3)對s2.1.2.1細化對s2.1.2.1來說,cocks及hens都已確定,這時的chicks可以計算出:chicks=100-cocks-hens式(1)已滿足。只要用式(2)指定的條件測試便可知這一組cocks,hens及chicks是否滿足題意。滿足則打印出一組解。于是s2.1.2.1可細化為:圖3.28為進一步細化的百錢買百雞算法。s2.1.2.1:chicks=100-cocks-hens;if(5*cocks+3*hens+chicks/3.0)==100)printf(″%d%d%d\n″,cocks,hens,chicks);
圖3.28百錢買百雞算法的細化算法
當(cocks<=19)當(hens<=33)cocks=0cocks++hens=0chicks=100-cocks-henshens++(5*cocks+3*hens+chicks/3.0)==100輸出一組cocks,hens和chichs(空)真假這一小節(jié)介紹了應用重復結(jié)構(gòu)的典型問題及算法思想,下面介紹如何用C語言提供的三種重復語句來實現(xiàn)有關(guān)算法。3.4.2while循環(huán)結(jié)構(gòu)
while是一種循環(huán)結(jié)構(gòu),其形式如下:while(表達式)循環(huán)體語句循環(huán)體在執(zhí)行while語句時,先對判斷表達式進行計算,若其值為真(非0),則執(zhí)行循環(huán)體語句;否則跳過循環(huán)體,而執(zhí)行該結(jié)構(gòu)后面的語句。在進入循環(huán)體后,每執(zhí)行完一次循環(huán)體語句后,再對判斷表達式進行一次計算和判斷。當發(fā)現(xiàn)判斷表達式的值為0時,便立即退出循環(huán)。下面介紹這種重復結(jié)構(gòu)的應用。程序執(zhí)行結(jié)果:
Populationafter10yersis:13.262353執(zhí)行完這個程序,得到一個結(jié)果后,人們第一個念頭就是先要確認這個結(jié)果是否對,即這個程序有沒有問題。對于這樣的問題,一個可靠的辦法是同時進行一次手算,來比較兩個結(jié)果是否一致。顯然,當程序重復次數(shù)較少時,這樣的驗證還是有可能的。如對于本例來說,可以稍做修改,變成下面的程序:/******文件名:ex031702.c******//******計算人口增長******/#include<stdio.h>intmain(void){doublem=13;inti=1; while(i<=10){ m=m*(1+0.002); printf(“Populationafter%dyersis:%f\n”,i,m); i++; } return0;}重新執(zhí)行得到如下結(jié)果:針對這個結(jié)果,進行分析,如果每個數(shù)據(jù)都經(jīng)過了驗證,就可以確信程序正確了。但是,如果重復次數(shù)較多,進行完全的驗證就不現(xiàn)實了。有效的方法是選擇幾個有代表性的點進行演算。這種方法,在程序設計中稱為測試。當然,這樣的測試,充其量只能是“沒有發(fā)現(xiàn)程序中的錯誤”,而不能保證程序中沒有錯誤。關(guān)鍵是選擇好的測試點,以便發(fā)現(xiàn)更多的錯誤。例3.18Fibonacci算法C語言的程序?qū)崿F(xiàn)。根據(jù)圖3.19所示的算法。可以很容易地寫出如下程序。/******文件名:ex031801.c******//******計算Fibonacci數(shù)******/#include<stdio.h>intmain(void){intfib1=1,fib2=1,fib,i=3;while(i<=12){ fib=fib1+fib2; fib1=fib2;fib2=fib; i++; }printf(“TheFibonaccinumberafter1yeris:%d\n”,fib);return0;}運行結(jié)果如下:TheFibonaccinumberafter1yeris:144例3.19用牛頓迭代法計算一個正實數(shù)a的平方根,精確到E0=10-5。由3.4.1節(jié)可知,近似迭代算法有三個要素:迭代關(guān)系式、初值(猜測值)和精度。(1)建立迭代關(guān)系式,由題意可以寫出:x=√a或x2=a于是得到方程:f(x)=x2-a=0,f′(x)=2x根據(jù)牛頓迭代公式x=x-f(x)/f′(x)=x-(x2-a)/2x=1/2(x+a/x)即x=(x+a/x)*0.5這就是要建立的迭代關(guān)系式。(2)給定初值。今設√a的初值為a(當然也可以為其它值)。(3)給定精度(誤差)為E0。精度是迭代控制的條件,當誤差大于E0時,要繼續(xù)迭代。如何計算誤差呢?本來應該用|x-√a|來表示誤差,即把求出的x與真正的√a相比,計算出其誤差,但是現(xiàn)在√a并不知道,因此可換一種方法來表示誤差,用|x*x-a|(因為要求x=√a,即x2=a,x與a的差距可以通過x2與a的差距反映出來)。于是得到循環(huán)結(jié)構(gòu)的控制條件:|x2-a|≥E0即fabs(x*x-a)>=E0上述三個元素都已找到,參照圖3.23很容易寫出如下C函數(shù):/******文件名:ex031901.c******//******計算平方根******/#include<math.h>#defineE00.00005doublesq_root(doublea) /*計算a的平方根*/{doublex; x=a; x=(x+a/x)*0.5; while(fabs(x*x-a)>=E0) x=(x+a/x)*0.5; return(x);}這里fabs是一個標準數(shù)學函數(shù)名。E0是誤差要求,可以用#define命令在程序頭部作為一個符號常數(shù)進行定義,在不同情況下根據(jù)需要可以對它修改。#include<stdio.h>intmain(void){ doublef=2.0;printf("Therootof%fis%f\n",f,sq_root(f));return0;}用下面的主函數(shù)進行測試測試結(jié)果如下:Therootof2.000000is1.414216例3.20輸入多個數(shù),輸出其中最大者的C程序。根據(jù)圖3.25,可以得到如下C程序。/******文件名:ex032001.c******//******多數(shù)中取大******/#include<stdio.h>#defineFLAG-32768intmain(void){intmax,n;printf(“Inputanumber:”);scanf(“%d”,&n);max=n;while(n!=FLAG) { printf(“Inputnextnumber:”); scanf(“%d”,&n); if(n>max) max=n; } printf(“Themaxis:%d\n”,max); return0;}某次執(zhí)行情況如下:
這個例子中使用了“-32768”作為判斷循環(huán)結(jié)束的一個標志。當處理字符時,可以使用系統(tǒng)定義的標志“EOF”(操作Shift+z即^+z).例3.21將輸入的字符原樣輸出。/******文件名:ex032101.c******//******測試字符結(jié)束符******/#include<stdio.h>intmain(void){intc;while((c=getchar())!=EOF)putchar(c);return0;}執(zhí)行情況如下:eehh^z為了說明這一程序的功能,把它作如下改寫:/******文件名:ex032102.c******//******測試字符結(jié)束符******/#include<stdio.h>intmain(void){intc;c=getchar();while(c!=EOF){putchar(c);c=getchar();}return0;}程序中的“EOF”是一個符號常數(shù),稱為文件結(jié)束標志,它是在文件stdio.h中定義的:#defineEOF-1當從鍵盤輸入^Z(或遇文件結(jié)束標記),c的值得到-1,等于EOF。第二個程序中使用了兩個“c=getchar();”,第一個用以給c一個初值,以便進入while;第二個則再輸入一個字符。而第一個程序則把這兩次函數(shù)調(diào)用合并為一次,并作為條件表達式的一部分。這種簡潔的表達方式是C語言的一個特點。它減少了重復編碼,也就減少了出錯的機會,給程序維護也帶來了方便,下面再看一個例子。例3.22求解百錢買百雞的C程序。此程序可以按照圖3.27所示的算法寫出如下:/******文件名:ex032201.c******//******百錢買百雞問題******/#include<stdio.h>intmain(void){intcocks=0; printf("%8s%8s%8s\n","cocks","hens","chicks");while(cocks<=19) { inthens=0; while(hens<=33) { intchicks; chicks=100-cocks-hens; if(5*cocks+3*hens+chicks/3.0==100) printf("%8d%8d%8d\n",cocks,hens,chicks); hens++; } cocks++; }return0;}執(zhí)行結(jié)果如下:3.4.3do…while結(jié)構(gòu)do…while也是一種重復結(jié)構(gòu),其形式如下:當流程到達do后,立即執(zhí)行循環(huán)體一次,然后才對判斷表達式進行計算、測試。若表達式的值為真(非0),則重復執(zhí)行一次循環(huán)體;否則退出。與while結(jié)構(gòu)相比,do…while結(jié)構(gòu)至少要執(zhí)行一次循環(huán)體。這樣的結(jié)構(gòu)應用在需要事先執(zhí)行一次循環(huán)體的程序非常容易理解。do循環(huán)體while(表達式);如例3.21中第2個程序可以改寫為:/******文件名:ex032103.c******//******測試字符結(jié)束符******/#include〈stdio.h〉intmain(void){intc;do{c=getchar();putchar(c);}while(c!=EOF);return0;}同樣,例3.20中的程序可以改寫為:/******文件名:ex032002.c******//******多數(shù)中取大******/#include<stdio.h>#defineFLAG-32768intmain(void){intmax,n;do{ printf(“Inputanumber:”); scanf(“%d”,&n); if(n>max)max=n; }while(n!=FLAG); printf(“Themaxis:%d\n”,max); return0;}例3.19中的函數(shù)sqroot()也可以改寫為:/******文件名:ex031902.c******//******計算平方根******/#include<math.h>#defineE00.00005doublesqroot(doublea) /*計算a的平方根*/{doublex;x=a; dox=(x+a/x)*0.5; while(fabs(x*x-a)>=E0); return(x);}3.4.4for結(jié)構(gòu)為了說明for結(jié)構(gòu),先看下面一個程序段:sum=0;for(i=1;i<=n;i++)sum=sum+i;它是下面的控制結(jié)構(gòu)的一個例子。for(表達式1;表達式2;表達式3)循環(huán)體為了說明for后面括弧中三個表達式的語句,將它改寫為如下while結(jié)構(gòu):sum=0;i=1; /*相當于for語句中的表達式1*/while(i<=n) /*相當于for語句中的表達式2 */{sum=sum+i; /*for循環(huán)體 */i++; /*相當于for語句中的表達式3 */}顯然,這三個表達式的作用是控制循環(huán),故稱循環(huán)控制表達式。表達式1稱為初始化表達式;表達式2稱為條件表達式;表達式3稱為修正表達式??梢钥闯觯篺or語句是將初始化、條件判斷、循環(huán)變量值變化三者組織在一起的循環(huán)控制結(jié)構(gòu)。for結(jié)構(gòu)的格式可以表示為:for語句的執(zhí)行過程如下:(1)先執(zhí)行表達式1(初始化表達式)。注意在整個循環(huán)中它只執(zhí)行一次。(2)重復下面的過程:計算表達式2(判斷表達式),若為真(非0),就執(zhí)行一次循環(huán)體語句,然后再執(zhí)行表達式3(修正表達式);再計算表達式2(判斷表達式),判斷是否為“真”,……直至表達式2(判斷表達式)的值為假(0),就不再執(zhí)行循環(huán)體了。for(初始化表達式;判斷表達式;修正表達式)循環(huán)體語句在某些情況下,用for結(jié)構(gòu)表示循環(huán),顯得緊湊而清晰。尤其是它能利用表達式3自動地使循環(huán)變量值發(fā)生改變,不像while結(jié)構(gòu)那樣要在循環(huán)體中設置“修正操作”。實際上,for語句中的表達式3并不僅限于修正循環(huán)變量,而可以是任何操作。例如前面介紹的求1+2+…+n的程序段可以改寫為:形成一個沒有循環(huán)體的結(jié)構(gòu),或者說循環(huán)體是一個空語句(僅一個分號)。而表達式3成為一個逗號表達式,它包含兩個簡單表達式(包含了本應由循環(huán)體完成的功能),表達式的執(zhí)行順序從左到右。表達式1也是一個逗號表達式,它使sum和i都初始化。這個for語句的執(zhí)行流程也如圖3.25所示。for(sum=0,i=1;i<=n;sum=sum+i,i++);同樣,例3.19的函數(shù)sq_root()可以改用for語句寫為:但是,這樣的修改并沒有多大意義,反而使程序變得難于理解。而對于例3.18中計算Fibonacci數(shù)程序的修改,才體現(xiàn)了for結(jié)構(gòu)的優(yōu)點,它使程序更容易理解。修改如下:#include<math.h>#defineE00.00005doublesq_root(doublea) /*計算a的平方根*/{doublex;for(x=a,x=(x+a/x)*0.5;fabs(x*x-a)>=E0;x=(x+a/x)*0.5);return(x);}同樣,對例3.22的計算百錢買百雞問題的程序,用for結(jié)構(gòu),也更容易理解。/******計算Fibonacci數(shù)******//******文件名:ex031802.c******/#include<stdio.h>intmain(void){intfib1=1,fib2=1,fib,i;for(i=3;i<=12;i++) { fib=fib1+fib2; fib1=fib2; fib2=fib; }printf(“TheFibonaccinumberafter1yeris:%d\n”,fib);return0;}上面的例子說明,當使用計數(shù)方式控制重復過程時,采用for結(jié)構(gòu)比較方便。/******文件名:ex032202.c******//******百錢買百雞問題******/#include<stdio.h>intmain(void){intcocks;printf("%8s%8s%8s\n","cocks","hens","chicks");for(cocks=0;cocks<=19;cocks++) { inthens; for(hens=0;hens<=33;hens++) { intchicks; chicks=100-cocks-hens; if(5*cocks+3*hens+chicks/3.0==100) printf("%8d%8d%8d\n",cocks,hens,chicks); } }return0;}例3.23打印九九表(如圖3.28所示)。1 2 3 4 5 6 7 8 91 2 3 4 5 6 7 8 92 4 6 8 10 12 14 16 183 6 9 12 15 18 21 24 274 8 12 16 20 24 28 32 365 10 15 20 25 30 35 40 456 12 18 24 30 36 42 48 547 14 21 28 35 42 49 56 638 16 24 32 40 48 56 64 729 18 27 36 45 54 63 72 81
圖3.28一種九九乘法表
下面用逐步求精的方法分析本例的解法。首先,把上述九九表分為三部分,表頭(即1~9九個數(shù)字)、隔線、表體。于是,這個程序也可以分為如下三部分:·打印表頭;·打印隔線;·打印表體;(1)打印表頭。表頭有九個數(shù)字1,2,…,9??梢钥闯纱蛴∫粋€變量i的值,其初值為1,每次加1,直到9為止。這使用for結(jié)構(gòu)最合適。設每個數(shù)字區(qū)占4個字符空間,則很容易寫出s1:for(i=1;i<=9;i++)printf(″%4d″,i);(2)打印隔線。考慮隔線的總寬度與表頭同寬,則可以用同樣結(jié)構(gòu)寫出s2:for(i=1;i<=36;i++)printf(″%c″,′-′);在上述兩個程序段中,都使用i作循環(huán)變量。在s2中,i只用于控制循環(huán)過程,稱單純循環(huán)變量。在s1中,i除用于控制循環(huán)過程外,還作操作變量使用,即在循環(huán)體內(nèi)還要用到它,對其進行操作,稱為操作型循環(huán)變量。在使用單純循環(huán)變量時,循環(huán)變量本身的具體值并不重要,重要的是通過循環(huán)變量控制循環(huán)執(zhí)行的次數(shù)。例如,打印一行隔線也可以寫為:for(i=101;i<=136;i++)printf(″%c″,′-′);還可以寫為for(i=36;i>=1;i--)printf(″%c″,′-′);在眾多的書寫形式中,當然最易于理解的還是一開始寫的那種形式?;騠or(i=10;i<=360;i+=10)printf(″%c″,′-′);(3)打印表體。表體共九行,所以首先考慮一個打印九行的算法s3:for(i=1;i<=9;i++){打印第i行}下面進一步考慮如何“打印第i行”。因為每行都有九個數(shù)字,故“打印第i行”可以寫為s3.1:for(j=1,j<=9;j++)打印第j個數(shù)“打印第j個數(shù)”即在第i行的第j列上打印一個數(shù),大小為i*j,占4個字寬。故可寫為printf(″%4d″,i*j);在寫這個語句時,人們自然要考慮寫不寫換行符。顯然不能在每個數(shù)字后面都換行,而只能在第9個數(shù)字后面寫換行。實現(xiàn)只在第9個數(shù)字后面換行的辦法是,打印完一行,即在j循環(huán)后寫一個語句使換行:printf(″\n″);于是,打印九九表的程序可寫為:/******文件名:ex032301.c******//******打印九九乘法表******/#include<stdio.h>intmain(void){inti,j;for(i=1;i<=9;i++)printf("%4d",i);printf("\n");for(i=1;i<=36;i++)printf("%c",'-');printf("\n");for(i=1;i<=9;i++){for(j=1;j<=9;j++) printf("%4d",i*j); printf("\n"); }return0;}程序運行結(jié)果如下:在這個程序中,也可以利用條件表達式判斷在哪個數(shù)字后換行。把兩個打印函數(shù)合并為:printf((j==9)?″%4d\n″:″%4d″,i*j);這樣可以使得程序變得更為簡潔,也提高了效率,因為函數(shù)調(diào)用需要一定的時間。例3.24用梯形法求數(shù)值積分。定積分的幾何意義就是求曲線f(x)與直線y=0,x=a,x=b所圍成的曲頂梯形的面積。當能找到f(x)的原函數(shù)F(x)時,利用牛頓-萊布尼茲公式:
可以精確地求出I值來。當f(x)的原函數(shù)不易找到時,便可以借助數(shù)值方法近似地求出I的值。用數(shù)值方法計算定積分有兩個關(guān)鍵:一是將連續(xù)的對象分割為容易求解的一些子對象;二是用迭代法對迭代表達式反復操作。下面介紹一種容易理解的方法——梯形法。由圖3.29所示,一個曲頂梯形可以分割為許多小的寬為h的曲頂梯形。bf(x)x0yaa+ha+iha+ih+ha+2h……圖3.29用梯形法求定積分當h很小時,每個小的曲頂梯形都可以近似看作是梯形,第i個小曲頂梯形的面積近似為:si=h/2[f(a+ih)+f(a+(i+1)h)]令h=(b-a)/n于是有
當n→∞時,就能準確地求出定積分s。在現(xiàn)實中,只要取相當大的n,使誤差小于要求的值,就可以近似地計算出定積分s:s≈h2[f(a)+f(a+h)+f(a+h)+f(a+2h)+…+f(a+(n-1)h)+f(a+(n-1)h)+f(b)]=h/2[f(a)+f(b)]+用迭代形式描述為:for(i=1;i<=n-1;i++){s=0.5*hh(f(a)+f(b))s=s+h*f(a+i*h)}最后計算的結(jié)果,就是函數(shù)f(x)的定積分。下面是一個利用梯形法求函數(shù)f(x)=√4-x2的定積分的程序:/*文件名:ex032401.c*//***用梯形法求積分***/#include<stdio.h>doublef(doublex);intmain(void)/***求定積分***/{floata,b;doubles,h;intn,i;printf("inputintegralareaa&b:");/*輸入一個區(qū)間*/scanf("%f,%f",&a,&b);printf("inputn:"); /*輸入迭代次數(shù)*/scanf("%d",&n);h=(b-a)/n;s=0.5*h*(f(a)+f(b));以下是兩次運行記錄:inputintegralareaa&b:0,2inputn:1000thevalueis:3.141555inputintegralareaa&b:0,2inputn:10000thevalueis:3.1415913.4.5循環(huán)結(jié)構(gòu)的中途退出與重復周期的中途結(jié)束圖3.30為循環(huán)結(jié)構(gòu)發(fā)中途退出(loop-and-half)與一個循環(huán)周期的中途結(jié)束的示意圖。中途退出循環(huán)結(jié)構(gòu)就是使用break語句,將流程轉(zhuǎn)到該循環(huán)結(jié)構(gòu)的后面。循環(huán)周期的的中途結(jié)束就是在重復執(zhí)行到某一個循環(huán)周期的中間時,由于某種條件,不需要再執(zhí)行循環(huán)體中后面的語句,提前結(jié)束該周期,而進入下一循環(huán)周期,在C語言中需要使用continue實現(xiàn)。這兩種控制功能不僅用于while結(jié)構(gòu)中,也可以用在do…while和for重復結(jié)構(gòu)中。while(…){ … … if(…)break;… …}……while(…){ … … if(…)cotinue;… …}……
(a)循環(huán)結(jié)構(gòu)中途退出
(b)一個循環(huán)周期的中途退出圖3.30重復結(jié)構(gòu)中途退出與循環(huán)周期的中途結(jié)束1.中途退出循環(huán)結(jié)構(gòu)——break語句在3.3.3節(jié)中已經(jīng)使用過break語句將流程轉(zhuǎn)出switch結(jié)構(gòu)。break語句還可以將流程中途轉(zhuǎn)出任何一種循環(huán)結(jié)構(gòu)。例3.25驗證素數(shù)。驗證一個正整數(shù)n>3的數(shù)是否為素數(shù),一個最直觀的方法是,看在2~n/2中能否找到一個整數(shù)m能將n整除。若m存在,則n不是素數(shù);若找不到m,則n為素數(shù)。這是一個窮舉驗證算法。這個循環(huán)結(jié)構(gòu)用下列表達式控制:·初值:m=2·循環(huán)條件:m<=n/2·修正:m++即這是一個for結(jié)構(gòu)。它的作用是窮舉2~n/2中的各m值。循環(huán)體是判斷n是否可以被m整除。顯然,在這個過程中,一旦找到一個m可以整除n,則用該m后面的各數(shù)去驗證已經(jīng)沒有意義了,需要退出循環(huán)結(jié)構(gòu)。按上述分析可以寫出一個驗證正整數(shù)n是否為素數(shù)的函數(shù):/******文件名:ex032501.c******//******驗證素數(shù)******/#include<stdio.h>intmain(void){
intm,n,flag=1;printf("請輸入要測試的整數(shù):");scanf("%d",&n);for(m=2;m<=n/2;m++)if(n%m==0){flag=0;/*設置非素數(shù)標志*/break;/*一旦找到一個m,斷定該n非素數(shù),不需再驗證*/}flag?printf("%d是素數(shù)\n",n):printf("%d不是素數(shù)\n",n);return0;}例2寫出程序的運行結(jié)果
main()for(j=9-i;j>=1;j--)printf(“?”);for(i=1;i<=5;i++){inti,j,k;for(k=1;k<=2*i-1;k++)printf(“*”);}{printf(“\n”);}ijk118~1????????2
7~1???????1~3
36~1??????1~5
4?????5~11~7
54~1????1~9
外層循環(huán):圖形的行數(shù)內(nèi)循環(huán)一:每行前導空格數(shù)內(nèi)層循環(huán)二:每行的*數(shù)開始新行之前要換行找出j、k隨i變化的規(guī)律舉例break語句break語句和continue語句格式break;
功能從循環(huán)體內(nèi)跳出,結(jié)束循環(huán)。
舉例main(){…….for(r=1;r<=10;r++){area=pi*r*r;if(area>100)break;printf(“%f”,area);}……條件為真退出循環(huán)continue語句格式continue;
功能結(jié)束本次循環(huán)舉例main(){intn;for(n=100;n<=200;n++){if(n%3==0)continue;printf(“%d”,n);}輸出100~200之間不能被3整除的數(shù)例2判斷一個數(shù)n是否是素數(shù)。素數(shù)只能被1和它本身整除的數(shù)。如何判斷{2,3,…,n-1}之中的數(shù)都不是n的因子n{1,n}n是素數(shù)驗證:輸入n當j<=k時n%j!=0真j++假breakj=2k=sqrt(n)j==k+1真yes假no#include<math.h>{intn,j,k;while(j<=k){if(n%j!=0)j++;elsebreak;}if(j==k+1)printf(“%dyes”,n);elseprintf(“%dno”,n);}printf(“\nInputn:”);scanf(“%d”,&n);sqrt(n)j=2,k=sqrt(n);
退出循環(huán)時j取值j=k+1
break退出j<=k程序舉例yesnomain()正常退出例7輸入一行字符,統(tǒng)計出其中英文字母、空格、其它字符的個數(shù)。#include<stdio.h>main(){charc;intzm,kg,qt;zm=kg=qt=0;while((c=getchar())!=‘\n’){if(c>=‘a(chǎn)’&&c<=‘z’||c>=‘A’&&c<=‘Z’)zm++;elseif(c==‘’)kg++;elseqt++;}printf(“zm=%d,kg=%d,qt=%d”,zm,kg,qt);}程序舉例2.提前結(jié)束一個循環(huán)周期——continue語句例3.26輸出100~200間的所有素數(shù)。這個程序可以在例3.24的基礎上進行。當測試到某個n存在一個因數(shù)m時,用break跳出內(nèi)層循環(huán)復結(jié)構(gòu),同時在外層循環(huán)結(jié)構(gòu)中要跳過輸出語句,進入對下一個數(shù)的測試。按上述分析可以寫出一個驗證正整數(shù)n是否為素數(shù)的函數(shù):/******文件名:ex032601.c******//******輸出素數(shù)******/#include<stdio.h>intmain(void)
{intm,n,flag;printf("\nTheprimersfrom100to200is:\n");for(n=101;n<=200;n+=2)/*僅測試100~200間的奇數(shù)*/{flag=1; /*設置標志 */for(m=2;m<=n/2;m++){if(n%m==0){flag=0; /*改變標志 */break;/*跳出內(nèi)層循環(huán)結(jié)構(gòu)*/}}if(flag==0) /*判斷標志 */continue;/*跳出過輸出語句進入下一周期*/printf(“%d,”,n);}printf("\n");return0;}
程序輸出如下:習題
3.1用C語言描述下列命題。(1)a小于b或小于c。(2)a或b都大于c。(3)a和b中有一個小于c。(4)a是非正整數(shù)。(5)a是奇數(shù)。(6)a不能被b整除。(7)角A在第一或第三象限。(8)a是一個帶小數(shù)的正數(shù),而b是一個帶小數(shù)的負數(shù)。3.2寫出下列表達式的值。(1)1<4&&4<7(2)1<4&&7<4(3)!(2<=5)(4)!(1<3)||(2<5)(5)!(4<=6=&&(3<=7)3.3若x=3,y=z=4,求下列表達式的值。(1)(z>=y>=x)?1:0(2)z>=y&&y>=x3.4若x=3,y=2,z=1,求下列表達式的值。(1)x<y?y:x(2)x<y?x++:y++(3)z+=(x<y?x++:y++)3.5寫出下面程序段的輸出結(jié)果。(1)intx=40,y=4,z=4;x=y==z;printf(″%d\n″,x);x=x==(y=z);printf(″%d\n″,x);(2)intx,y,z;x=y=z=0;++x||++y&&++z;printf(″x=%d\ty=%d\tz=%d\n″,x,y,z);x=y=z=0;++x&&++y||++z;printf(″x=%d\ty=%d\tz=%d\n″,x,y,z);x=y=z=0;++x&&++y&&++z;printf(″x=%d\ty=%d\tz=%d\n″,x,y,z);x=y=z=-1;++x&&++y&&++z;printf(″x=%d\ty=%d\tz=%d\n″,x,y,z);x=y=z=-1;++x&&++y||++z;printf(″x=%d\ty=%d\tz=%d\n″,x,y,z);x=y=z=-1;++x||++y&&++z;printf(″x=%d\ty=%d\tz=%d\n″,x,y,z);(3)intx=1,y=1,z=1;y=y+z;x=x+y;printf(″%d\n″,x<y?y:x);printf(″%d\n″,x<y?x++∶y++);printf(″%d\n″,x);printf(″%d\n″,y);x=3;y=z=4;printf(″%d\n″,(x>=y>=x)?1∶0);printf(″%d\n″,z>=y&&y>=x);(2)#include<stdio.h>intmain(void){intvalue1,value2,sum;value1=50;value2=25;sum=value1+value2;printf(″Thesumof%dand%dis%d\n″,value1,value2,sum);return0;}3.6分別用流程圖、N-S圖描述下列程序,并寫出程序的執(zhí)行結(jié)果。(1)(2)#include<stdio.h>intmain(void){inta;scanf(″%d″,&a);if(a>=0)printf(″plus\n″);elseprintf(″minus\n″);return0;}(3)#include<stdio.h>intmain(void){intx,y=1,z;if(y!=0)x=5;printf(″x=%d\t″,x);if(y==0)x=3;elsex=5;printf(″x=%d\t\n″,x);z=-1;if(z<0)if(y>0)x=3;elsex=5;printf(″x=%d\t\n″,x);if(z=y<0)x=3;elseif(y==0)x=5;elsex=7;printf(″x=%d\t″,x);printf(″z=%d\t\n″,z);if(x=z=y)x=3;
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 幼兒園大班社會活動《課間十分鐘》教案(5篇)
- 2025年重慶市安全員知識題庫及答案
- 莆田學院《數(shù)據(jù)結(jié)構(gòu)(Java)》2023-2024學年第二學期期末試卷
- 天津中德應用技術(shù)大學《商務數(shù)據(jù)分析》2023-2024學年第二學期期末試卷
- 濰坊學院《土地測量與評價》2023-2024學年第二學期期末試卷
- 邯鄲科技職業(yè)學院《風電機組設計與制造》2023-2024學年第二學期期末試卷
- 長治幼兒師范高等??茖W校《預算管理模擬》2023-2024學年第二學期期末試卷
- 2025年江西省建筑安全員《B證》考試題庫
- 2025年湖南省安全員《A證》考試題庫及答案
- 揚州環(huán)境資源職業(yè)技術(shù)學院《通風空調(diào)A》2023-2024學年第二學期期末試卷
- 區(qū)塊鏈應用操作員技能大賽考試題庫大全-下(多選、判斷題)
- 二 《“友邦驚詫”論》(同步練習)解析版
- 施工技術(shù)交底(電氣安裝)
- 污水處理廠TOT項目招標文件模板
- 勞工及道德體系法律法規(guī)清單
- 倉儲物流中心物業(yè)管理服務費報價單
- 2024年哈爾濱科學技術(shù)職業(yè)學院單招職業(yè)適應性測試題庫及答案解析
- 2024年北京市大興區(qū)清源街道招聘筆試沖刺題(帶答案解析)
- (2024年)污水處理設備培訓方案
- 《生物質(zhì)熱電聯(lián)產(chǎn)工程設計規(guī)范》
- 中國十五冶招聘線上筆試測評題庫
評論
0/150
提交評論