360 likes | 450 Views
應用程式. 我們學習程式設計就是希望使用電腦,將電腦當成一個有效的工具,當成一個忠實的僕人,能幫我們解決一些問題,您可能會想到底那些問題可請電腦解決呢?一般來講,很難的計算問題,很繁(煩)的問題,需要電腦提醒的問題等等,幾乎您能想到的問題都可請電腦幫您處理。 電腦怎樣處理您的問體呢?因為它是一個僕人,聽命於主人的您,您下達一個指令它就幫您執行這個指令,您再下達另一個指令它就幫您執行另一個指令,您若不下達指令它也就無從執行,而賦閒休息了。.
E N D
應用程式 • 我們學習程式設計就是希望使用電腦,將電腦當成一個有效的工具,當成一個忠實的僕人,能幫我們解決一些問題,您可能會想到底那些問題可請電腦解決呢?一般來講,很難的計算問題,很繁(煩)的問題,需要電腦提醒的問題等等,幾乎您能想到的問題都可請電腦幫您處理。 • 電腦怎樣處理您的問體呢?因為它是一個僕人,聽命於主人的您,您下達一個指令它就幫您執行這個指令,您再下達另一個指令它就幫您執行另一個指令,您若不下達指令它也就無從執行,而賦閒休息了。 正修科技大學計算機中心
這些您所下達的指令集合起來就稱為一個程式。您使用 C 語言下達指令的集合稱為 C 語言原始程式(source program),原始程式是人們看得懂的程式,但電腦看不懂,看不懂的指令當然就無法執行了,必須請人翻譯成電腦看得懂的指令才行,負責翻譯的程式稱為編譯器(compiler),電腦看得懂的指令所構成的程式稱為目的程式(object program),所以電腦所執行的是目的程式。 本章就舉幾個日常生活上會碰到的問題,說明如何請電腦幫我們解決。 正修科技大學計算機中心
13.1 二月平均日溫 • 我們想了解高雄市在西元 2005 年 2 月的每日平均溫度幾度?這個資料我們可以上氣象局的網站查一查就可了解。我們查到的是如下的一張圖: • 紅色的曲線表示「日均溫」,左邊的垂直軸表示攝氏溫度刻度,水平向右的軸線表示「日數」,我們用目視法製成如下的資料表: 正修科技大學計算機中心
表 13.1 高雄站二月日均溫 日數 日均溫(攝氏溫度) ------ -------------------------- 1 16.5 3 18.0 5 22.5 7 24.0 9 22.5 11 20.5 13 19.0 15 21.0 17 26.0 19 17.0 21 13.0 23 22.0 25 23.0 27 21.0 28 18.0 正修科技大學計算機中心
現在我們要利用這些資料,設計一個查詢程式,您從鍵盤輸入一個日數,電腦就回應給您一個高雄市 2005 年 2 月當日的平均溫度。 我們可以將上表設計成兩個一維陣列,「日數」設計成一維陣列 days,包含 15 個元素,每個元素均為整數 int,如下: int days[15] = {1,3,5,7,9,11,13,15,17,19,21,23,25,27,28}; 「日均溫(攝氏溫度)」設計成一維陣列 degs,包含 15 個元素,每個元素均為浮點數 float,如下: float degs[15] = {16.5, 18.0, 22.5, 24.0, 22.5, 20.5, 19.0, 21.0, 26.0, 17.0, 13.0, 22.0, 23.0, 21.0, 18.0}; 正修科技大學計算機中心
當我們從鍵盤輸入一個日數,取名為 day,例如您輸入 13,也就是說變數 day 的值為 13。然後 day 與 days 陣列裡的每一個元素比較,分別比較 days[0]、days[1]、days[2]、...、days[14],結果在 days[6] 找到,那麼在 degs 陣列的相對位置 degs[6] 就是我們要的日均溫 19.0 攝氏度。 但若您從鍵盤輸入一個日數 14 又如何?14 在 days 陣列從頭到尾都找不到,那怎麼辦呢?因為表中只有 13 日及 15 日的日均溫,並沒有 14 日的日均溫,這時可用內插法求出: 14日的日均溫 = (13日的日均溫 + 15日的日均溫) / 2 = (19.0 + 21.0) / 2 = 20.0 因此可得 14 日的日均溫為 20.0 攝氏度。 正修科技大學計算機中心
二月日均 溫流程圖 正修科技大學計算機中心
程式 daydeg.c 從鍵盤輸入一個日數 day,必須在 1 至 28 之間才合乎規定,若不在此範圍就必須從新輸入,一直到合乎規定程式才會往下執行的,這是保護您獲得正確答案的一個措施,否則若輸入 31,因為二月份根本沒有 31 日,顯然不合理,萬一您接受此 31 日,您的程式根本無法執行。 您設計的檢查條件可以這樣寫: day<1 || day>28 符合這個條件表示您輸入的日數 day 是錯誤的。 正修科技大學計算機中心
您設計的檢查條件可以這樣寫: day<1 || day>28 符合這個條件表示您輸入的日數 day 是錯誤的。可是這樣寫就缺乏彈性了,因為只有二月份才有 28 天,若三月四月又如何?這時您可利用陣列的特性,直接指明陣列 days[] 的最後一個元素值,days[] 陣列的元素個數可透過 sizeof(days) 取得,本例題其值為 15,從零算起最後一個就是第十四個,以 sizeof(days)/sizeof(int)-1 表示,因此條件可改寫如下: day<days[0] || day>days[sizeof(days)/sizeof(int)-1] 您所輸入的日數 day 輪番在 days 陣列找尋,找到時就跳出迴圈,輸出答案,找不到時就用內插法計算日均溫後輸出。 正修科技大學計算機中心
【程式daydeg.c執行結果】 請輸入一個二月份日數(1-28): 13 <Enter> i=0 days[i]= 1 degs[i]=16.5 i=1 days[i]= 3 degs[i]=18.0 i=2 days[i]= 5 degs[i]=22.5 i=3 days[i]= 7 degs[i]=24.0 i=4 days[i]= 9 degs[i]=22.5 i=5 days[i]=11 degs[i]=20.5 i=6 days[i]=13 degs[i]=19.0 二月13日的日均溫為 19.0 攝氏度 【程式daydeg.c執行結果】 請輸入一個二月份日數(1-28): 14 <Enter> i=0 days[i]= 1 degs[i]=16.5 i=1 days[i]= 3 degs[i]=18.0 i=2 days[i]= 5 degs[i]=22.5 i=3 days[i]= 7 degs[i]=24.0 i=4 days[i]= 9 degs[i]=22.5 i=5 days[i]=11 degs[i]=20.5 i=6 days[i]=13 degs[i]=19.0 i=7 days[i]=15 degs[i]=21.0 二月14日的日均溫為 20.0 攝氏度 正修科技大學計算機中心
13.2 求函數的根 • 二月日均溫的例題 daydeg.c 是在一個表格裡找尋資料,但在商業或工程方面常常要從一條已知的曲線(函數)求出滿足條件的值。例如有一顆球從一百公尺高的地方自由落下,球因為受到地心引力 g 的影響,愈接近地面它的速度愈快,距離 s 與時間 t 以及地球重力加速度 g 的關係如下所示: 時間 t 以秒計,地球重力加速度 g 為 9.8 每秒每秒公尺計算,因此距離 s 的單位就是公尺了。 正修科技大學計算機中心
我們將此曲線繪製圖形,水平座標為時間 t 座標,垂直座標為距離 s 座標,向下為正方向,時間 t 為 0 時,距離 s 的值為 0,原點座標為 (0,0) 。請問距離為 100 時 t 的值為何? s = 0.5 * g * t * t [註]「*」表示乘法 100 = 0.5 * 9.8 * t * t 相當於如下的函數 f(t): f(t) = 0.5*9.8*t*t - 100 = 0 我們只要求出一個 t 滿足 f(t)=0 就可以了,可以設計如下的函式: double f(double t) { return (0.5*9.8*t*t-100.0); } 已知一個參數 t 值,傳回 (0.5*9.8*t*t-100.0) 函式值。 正修科技大學計算機中心
我們可設計一個簡單的程式對於 f(t) 函數取幾個點看看,t 從 0 變化至 9 每次增加 1,看看 f(t) 值的變化如何? 從 balldrop.c 執行結果可以看出當 t=4 時 f(t) 函數值為負數 -21.6,但當 t=5 時函數 f(t) 值為正數 22.5,可見 t 在 4 與 5 之間有某一個值可以滿足函數 f(t)=0 的條件,這個 t 就是我們所要的時間。 正修科技大學計算機中心
【程式balldrop.c】 /******************* balldrop.c ********************/ #include <stdio.h> double f(double t) { return (0.5*9.8*t*t-100.0); } int main() { int i; for (i=0; i<10; i++) printf("t=%d f(t)=%6.1f\n", i, f((double)i)); return 0; } 【執行結果】 t=0 f(t)=-100.0 t=1 f(t)= -95.1 t=2 f(t)= -80.4 t=3 f(t)= -55.9 t=4 f(t)= -21.6 t=5 f(t)= 22.5 t=6 f(t)= 76.4 t=7 f(t)= 140.1 t=8 f(t)= 213.6 t=9 f(t)= 296.9 正修科技大學計算機中心
計算球落地 秒數流程圖 正修科技大學計算機中心
既然知道 t 在 4 與 5 之間有某一個值可以滿足函數 f(t)=0 的條件,這個 t 就是我們所要的秒數,問題是您想求到小數幾位呢?就求到小數四位吧,設第一個時間為 t1 其值為 4,第二個時間為 t2,每次增加 dt=0.0001,也就是將 t 在 4 與 5 之間劃分一萬個小格,每一個小格的距離 0.0001,這個值在我們的程式裡稱為 dt,d 是 delta 很小的意思,t 是 time 時間的意思,dt 是很小時間增量的意思,「0.0001」也就是萬分之一秒。 當時間 t1 時,它的函數值為 f1=f(t1),當時間 t2 時,它的函數值為 f2=f(t2),我們只要判斷 f1 的符號是否與 f2 的符號相異(一個負號,另一個正號),若是相異則表示 t 在 t1 與 t2 的中間,計算 (t1+t2)/2 就是我們要的秒數。 正修科技大學計算機中心
【程式droptime.c】 /******************** droptime.c *****************/ #include <stdio.h> double f(double t) { return (0.5*9.8*t*t-100.0); } int main() { double t1=4, dt=1.0/10000, t2=t1+dt, f1, f2; int step = 1; f1 = f(t1); f2 = f(t2); while (f2*f1>0.0) { t1 = t2; f1 = f2; t2 = t1 + dt; f2 = f(t2); step++; } printf("經 %7.5f 秒後球落地, 共走 %d 步.\n", (t1+t2)/2, step); return 0; } 【執行結果】 經 4.51755 秒後球落地, 共走 5176 步. 正修科技大學計算機中心
13.3 二分搜尋法 • 前面的例題,動不動都要執行幾千步,甚或執行幾萬步,有沒有更先進的方法只要執行幾步,或幾十步就可找到答案的呢?我們在第七章介紹過一個二分搜尋法,針對陣列搜尋,現在我們要將它應用來找函數的根。 • 現在我們來看另一個題目,求滿足 f(x)=x*x*x-3*x+1=0 的 x 值。我們用目視法可知 f(0)=1,f(1)=-1,f(0)*f(1)<0,也了解 x 在 0 與 1 之間有一個值滿足 f(x)=x*x*x-3*x+1=0 的條件,它就是我們要找的根,左邊的界限稱為 xl,右邊的界線稱為 xr,中間的稱為 xm=(xl+xr)/2 是左右邊的平均值。現在的起步是 xl=0,xr=1,計算出 xm=(0+1)/2=0.5,我們想求到小數六位,為很小的值稱為 eps,它的值為 0.000001。 • 現在我們要來設計一個程式,為了保持彈性將這三個值 xl、xr、eps 從鍵盤輸入。程式的流程圖如下所示。 正修科技大學計算機中心
流程圖 findroot 求滿足 f(x)=x*x*x-3*x+1=0 的x值 正修科技大學計算機中心
執行 findroot.c 程式時,已知在 xl=0.0 與 xr=1.0 之間有根,就猜其中點 xm=(xl+xr)/2=0.5。 正修科技大學計算機中心
這時 f(0.5) 與 f(1.0) 同為負號,我們的目標不在 0.5 與 1.0 之間,遂將新的右邊界移到中點 0.5 之處,也就是新的 xr=0.5,這時 xl 左邊界的值仍為 0.0,如此範圍已經縮小一半了。 正修科技大學計算機中心
跟據 xl=0.0 與 xr=0.5 計算一個新中點 xm=(0.0+0.5)/2=0,25,這時函數值 f(0.25) 與 f(0) 同為正號,我們的目標不在 0.0 與 0.25 之間。遂將新的左邊界移到中點 0.25 之處,也就是新的 xl=0.25,這時 xr 右邊界的值仍為 0.5,如此範圍又縮小一半了,只剩下原來的四分之一了。 正修科技大學計算機中心
同樣的步驟繼續進行,不是拋掉右邊一半,就是拋掉左邊一半,範圍成等比級數縮小,速度相當快,求至小數六位只需猜二十次就可得答案了。最後的答案 xm=0.3472967,滿足 f(0.3472967)=0。 正修科技大學計算機中心
/********************* findroot.c ******************/ #include <stdio.h> double f(double x) { return (x*x*x-3.0*x+1.0); } int main() { double xl, xr, xm, eps; int step=0; printf("請輸入 xl,xr,eps (0 1 0.000001) : "); scanf("%lf %lf %lf", &xl,&xr,&eps); do { xm = (xl+xr)/2.0; step++; printf("xm=%8.6f f(xm)=%10.6f\n", xm, f(xm)); if (f(xm)*f(xl)>0) xl=xm; if (f(xm)*f(xr)>0) xr=xm; } while (xr-xl>eps && f(xm)!=0.0); printf("xm=%9.7lf 共執行 %d 次.\n", xm, step); return 0; } 正修科技大學計算機中心
【執行結果】 請輸入 xl,xr,eps : 0 1 0.000001 <Enter> xm=0.500000 f(xm)= -0.375000 xm=0.250000 f(xm)= 0.265625 xm=0.375000 f(xm)= -0.072266 xm=0.312500 f(xm)= 0.093018 xm=0.343750 f(xm)= 0.009369 xm=0.359375 f(xm)= -0.031712 xm=0.351563 f(xm)= -0.011236 xm=0.347656 f(xm)= -0.000949 xm=0.345703 f(xm)= 0.004206 xm=0.346680 f(xm)= 0.001627 xm=0.347168 f(xm)= 0.000339 xm=0.347412 f(xm)= -0.000305 xm=0.347290 f(xm)= 0.000017 xm=0.347351 f(xm)= -0.000144 xm=0.347321 f(xm)= -0.000064 xm=0.347305 f(xm)= -0.000024 xm=0.347298 f(xm)= -0.000003 xm=0.347294 f(xm)= 0.000007 xm=0.347296 f(xm)= 0.000002 xm=0.347297 f(xm)= -0.000001 xm=0.3472967 共執行 20 次. 正修科技大學計算機中心
13.4 輸出日期及時間 • 我們在日常生活裡常常會用到日期與時間,電腦輸出的報表也免不了註記日期及時間,因此如何使用函式庫裡有關日期及時間的函式顯得特別重要。因此先要了解有那些工具可以使用。 • 有關日期及時間的型態(type)及函式均存於 include 目錄 time.h 檔案裡,首先讓我們來了解幾個型態。 • 資料型態 time_t 定義於 time.h 表頭檔,它屬於算術型態,用來表示日曆時間,透過 time() 函式取得時間,函式 localtime() 及 gmtime() 也都使用 time_t 型態的變數取得時間的。ctime() 函式更是使用 time_t 型態的變數取得日期與時間的。 正修科技大學計算機中心
但 localtime() 及 gmtime() 函式傳回來的卻是 tm 的結構,tm 定義於表頭檔 time.h 裡,以 gcc 編譯器為例,tm 結構定義如下: struct tm { int tm_sec; /*秒(0-59)*/ int tm_min; /*分(0-59)*/ int tm_hour; /*時(0-23), 0表午夜*/ int tm_mday; /*日(1-31)*/ int tm_mon; /*月(0-11), 0表一月*/ int tm_year; /*年(目前之年數-1900)*/ int tm_wday; /*星期日數(0-6, 0為星期日)*/ int tm_yday; /*一年中日數(0-365, 元月一日為0)*/ int tm_isdst; /*日光節約時間為非0,否則為0*/ char *__tm_zone; /*時區*/ int __tm_gmtoff; }; 正修科技大學計算機中心
在 gmtime.c 程式裡我們設計一個 getdatetime() 函式,因為我們想提供給其他的程式使用,設計成函式比較方便使用,dt 必須最少 36 個字元的陣列才行,本例題宣告為八十個字元的陣列,其前冠上 static 關鍵字,是想離開函式後 dt 仍然存在才能構傳回給呼叫者。 char *getdatetime() { static char dt[80]; time_t now; time(&now); strftime(dt,36,"格林威治時間 %Y/%m/%d %H:%M:%S\n", gmtime(&now)); return dt; } 呼叫 time() 函式取得目前時間存入 now 變數,再透過 strftime() 函式將所取得的時間格式化後存入 dt 陣列傳回。 正修科技大學計算機中心
【程式gmtime.c】 /*********************** gmtime.c ******************/ #include <stdio.h> #include <stdlib.h> #include <time.h> char *getdatetime() { static char dt[80]; time_t now; time(&now); strftime(dt,36,"格林威治時間 %Y/%m/%d %H:%M:%S\n", gmtime(&now)); return dt; } int main(void) { printf(getdatetime()); return 0; } 【執行結果】 格林威治時間 2005/03/18 06:17:58 正修科技大學計算機中心
程式 gmtime.c 所輸出的時間為英國格林威治時間,是世界上的標準時間,但地球經度每十五度就相差一個小時,以台灣地區而言,台灣在英國的東方,較英國早八個小時看到太陽,因此格林威治時間必需加上八小時才是中原標準時間,執行結果 06:17:58 在台灣應該 14:17:58 才對。您自己去調整時間也未嘗不可,但何不用現成的 localtime() 函數以取代 gmtime() 函式呢? 將 gmtime.c 程式稍做修改,以輸出當地時間,如 localtime.c 所示。 正修科技大學計算機中心
【程式localtime.c】 /**************** localtime.c *************/ #include <stdio.h> #include <stdlib.h> #include <time.h> char *getdatetime() { static char dt[80]; time_t now; time(&now); strftime(dt,36,"%Y/%m/%d %H:%M:%S\n", localtime(&now)); return dt; } int main(void) { printf(getdatetime()); return 0; } 【執行結果】 2005/03/18 14:42:00 正修科技大學計算機中心
13.5 解聯立方程式 • 聯立方程式計算起來很費力又費時,最適合借助電腦了,但您要將解法告訴電腦才行,我們先舉一個簡單的例子,一步一步解出來,再寫程式告訴電腦。 • x1 + 2 x2 = 13 ---------- (1) • x1 - 3 x2 = 3 ---------- (2) • 上面是一個二元聯立方程式,兩個變數 x1 及 x2,將其係數命名為 A 矩陣(matrix),是二列二行的矩陣,將變數命名為 X 矩陣,二列一行,將等號右邊的係數命名為 B 矩陣,二列一行,可改寫如下:A 的反矩陣乘上 B 可得 X。 正修科技大學計算機中心
【程式inverse.c執行結果】 x[0]=9.000000 x[1]=2.000000 正修科技大學計算機中心
您要求解其他的聯立方程式,只需修改三個值,N、a、b,然後重新編譯後再執行就可以了。例如您要解下列的聯立方程式: 8 x1 + 2 x2 + 3 x3 = 30 ----------- (1) x1 - 9 x2 + 2 x3 = 1 ----------- (2) 2 x1 + 3 x2 + 6 x3 = 31 ----------- (3) 您只需修改: #define N 3 double a[N][N]={{8,2,3},{1,-9,2},{2,3,6}}; double b[N]={30,1,31}; 重新編譯後,執行結果如下: x[0]=2.000000 x[1]=1.000000 x[2]=4.000000 正修科技大學計算機中心