111 年 國立中央大學機械工程系光機電工程碩士班甲組《程式設計》
第 1 題20 分
如圖所示有一個燈具,上面有五個燈泡編號 ADCEF,但只有一個開關,想要用開關的開關順序做到,第一次開時 ADCEF 五個燈全亮,關掉再開變成BCDE四個燈亮,第三次則是EAC亮,第四次則為BD亮,第五次只有A亮。關掉再開又回到第一次。利用微控制器撰寫輪詢程式,偵測開關HI為ON,LO為OFF,計數開關次數,並依據次數控制控制 A,B,C,D,E 各顆燈炮的亮暗,HI為亮,LO為暗,執行開關及燈泡控制可以視為呼叫函式達成,不用擔心細節是否正確。
🖼️【此處有附圖,請對照原卷】
圖1 五燈泡燈具
登入後即可作答並保存紀錄。
核心觀念
本題考查微控制器的輪詢(polling)、開關邊緣偵測與狀態控制。
重點如下:
- 開關狀態: 表示 ON, 表示 OFF。
- 只在開關由 的「上升邊緣」發生時,才將開關次數加一。
- 開關持續保持 時,不可重複計數。
- 開關次數依序為 ,第 5 次後再開啟時回到第 1 種狀態。
- 燈泡輸出也是數位訊號: 為亮, 為暗。
解題方法
使用變數記錄:
prevSwitch:前一次輪詢讀到的開關狀態。switchCount:目前累積的開關次數。
當前狀態為 currentSwitch 時,若符合:
表示剛完成一次開啟動作,將計數器加一:
由於只有五種燈號狀態,因此可令:
各次開啟時的燈泡狀態如下:
| 開啟次數 | A | B | C | D | E |
|---|---|---|---|---|---|
| 1 | 亮 | 亮 | 亮 | 亮 | 亮 |
| 2 | 暗 | 亮 | 亮 | 亮 | 亮 |
| 3 | 亮 | 暗 | 亮 | 暗 | 亮 |
| 4 | 暗 | 亮 | 暗 | 亮 | 暗 |
| 5 | 亮 | 暗 | 暗 | 暗 | 暗 |
關閉開關時,五顆燈全部熄滅;再次由 變成 時,依新的計數值顯示下一種狀態。
關鍵程式碼
第 2 題10 分
超長整數四則運算,利用4個元素的16位元整數矩陣,每個元素記錄 滿 進位,4個元素可以表達 的超長正整數,同一個空間也可以分割為正負整數,利用 最小溢位值補數(即比最大值大 1 的數)方式來記錄負值,例如 為 , 而 即 ,但因為 無法用四個元素的 位元整數矩陣來表示,可以先扣掉 在執行完成後再加回 ,即 。
- 請撰寫一個函式可以取原值的補數
void GetComp(int* src, int* comp)其中src為原矩陣,comp為補數矩陣 (10%)。 - 由於最大正數頂多為 的一半,只到達 ,小於 因此只要一個數的最高元素為 就一定是負數,此即正負值判斷方式。請撰寫一個函式可以判斷輸入的正負並算出其絕對值。
void SingnABS(int* src, int* sing, int* abs),其中src為原矩陣,sing為正負號 為正 為負,abs為絕對值矩陣 (5%)。 - 最小溢位值補數的特點是加法可以不理會運算元的正負值直接相加,例如 , ,其中 溢位後自動消失。請撰寫一個函式可以將兩個輸入值相加。
void add(int* a, int* b, int*c)其中 為運算元矩陣, 為結果矩陣 (10%)。 - 配合補數運算及加法即可來執行減法請撰寫一個函式可以將兩個輸入值相減。
void subtract(int* a, int* b, int* c)其中 為運算元矩陣, 為結果矩陣 (5%)。
登入後即可作答並保存紀錄。
本題考驗的是如何使用陣列來模擬大數運算,特別是針對帶有符號的整數。核心概念是「二補數」(Two's Complement) 的概念,以及如何將這種表示法應用到以陣列儲存的數字上。由於題目限定使用 4 個 16 位元整數來表示一個大數,每個元素範圍是 ,這意味著我們實際上是在處理一個 位元的大數,但每個「位」是 進位的。
核心概念:
-
表示法:一個大數 由一個 4 個元素的陣列 表示,其中 的值在 之間。
這個表示法類似於 BCD (Binary Coded Decimal) 的概念,但基數是 而非 或 。 -
二補數 (Two's Complement):
- 正數:直接以其值儲存。
- 負數:
- 取其絕對值的「補數」。
- 補數的計算:對於一個 位元的系統,一個數 的補數是 。
- 在本題中,整個大數的「最大值」是 。題目說「最小溢位值補數 (即比最大值大 1 的數)」,這指的是 。
- 所以,一個負數 的二補數表示為 。
- 題目提到 的表示為 。這表示 。
- (一個 1 後面跟 16 個 0)。
- 。
- 這與題目的表示法 , 匹配。
- 可以看作是 。
- 在 4 個 16 位元元素 (每個 ) 的表示法中, 是無法直接表示的,因為它會溢出到第 5 個元素。
- 題目說「可以先扣掉 1 在執行完成後再加回 1」。這意味著,當我們計算 時,我們實際上計算 。
- 。
- 所以, 的表示為 。
- 例如 :。
- 。
- 。這與題目一致。
- 因此,一個負數 的二補數表示是 ,其中 。
-
溢位處理:由於我們只有 4 個元素,任何計算結果如果需要第 5 個元素來表示,則該第 5 個元素(最高位)會被忽略(溢位)。在加法中,這正好實現了二補數的循環特性。
Part 1: void GetComp(int* src, int* comp) (10%)
-
目的:計算一個大數的補數。
-
解釋:
- 如果
src代表一個正數,則其補數是其負數表示。 - 如果
src代表一個負數,則其補數是其絕對值表示。 - 根據題意,一個數 的補數是 。
- 在這裡表示為 。
- 所以,我們需要從 中逐個元素減去
src的對應元素,然後最後再對整個結果加 1。 - 加 1 操作需要處理進位。
- 如果
-
演算法:
- 複製
{9999, 9999, 9999, 9999}到comp陣列。 - 逐個元素減去
src的值:comp[i] = 9999 - src[i]。 - 將
comp陣列加上 1。這需要從comp[3]開始,逐個元素加 1,並處理進位。comp[3] = comp[3] + 1;- 如果
comp[3] >= 10000,則comp[3] = 0,並向comp[2]進位 1。 comp[2] = comp[2] + carry;- 如果
comp[2] >= 10000,則comp[2] = 0,並向comp[1]進位 1。 - 依此類推。
- 如果最高位
comp[0]產生進位,則該進位被忽略(溢位)。
- 複製
-
範例:計算 的補數。
src = {0, 0, 0, 99}。comp初始化為 。- 逐個元素減去
src:comp[0] = 9999 - 0 = 9999comp[1] = 9999 - 0 = 9999comp[2] = 9999 - 0 = 9999comp[3] = 9999 - 99 = 9900
comp現在是 。- 加上 1:
comp[3] = 9900 + 1 = 9901。沒有進位。
- 最終
comp = {9999, 9999, 9999, 9901}。這與題目中 的表示一致。
Part 2: void SingnABS(int* src, int* sing, int* abs) (5%)
-
目的:判斷大數的正負,並計算其絕對值。
-
解釋:
- 題目的判斷方式是:如果最高元素
src[0]為 ,則該數為負數。 - 這是一個簡化的判斷方法,基於二補數的特性。在 進位制的四個元素表示中,如果最高位(即
src[0])大於等於 的一半,則為負數。 - 題目給了一個更簡化的判斷:只要
src[0] == 9999,就一定是負數。這個判斷是正確的,因為最大的正數是 (大約是 ) 左右,而 已經遠大於這個值。 - 如果
src[0] == 9999,則sing設為 1 (負數)。 - 否則,
sing設為 0 (正數)。 - 計算絕對值:
- 如果
src是正數,則abs就是src。 - 如果
src是負數,則abs是src的補數。我們可以使用GetComp函式來計算。
- 如果
- 題目的判斷方式是:如果最高元素
-
演算法:
- 檢查
src[0]:- 如果
src[0] == 9999(或者更精確地說,如果src[0] >= 5000且src[1],src[2],src[3]都是 或較大,則為負數。但題目給定簡單判斷src[0] == 9999,我們就照此判斷。更嚴謹的判斷是看最高位是否大於等於 的位置。 - 題目說「最高元素為 9999 就一定是負數」。這是一個簡化判斷。
- 更常見的二補數判斷方式是:如果最高位的符號位是 1,則為負。在本題的 進位制中,我們可以約定最高元素
src[0]的值來判斷符號。如果src[0]大於等於 (這個值在 4 個 16 位元元素中表示為 ),則為負數。 - 題目給的判斷是
src[0] == 9999。這表示我們只考慮以 為基數的表示法。 - 讓我們採用題目給的簡易判斷:
if (src[0] >= 5000)則為負數。為什麼是 5000?因為 。如果最高位的 進位值大於等於 ,則可以視為負數。 - 修正判斷:題目說「最高元素為 9999 就一定是負數」。這句話本身有些問題。通常,二補數的符號判斷是看最高位是否超過一半。在 4 個 16 位元元素的表示法中,總共有 位元。總範圍是 到 (如果最高位是符號位)。
- 在 進位制下,最大值是 。最小負數的絕對值是 。
- 表示 的方法是 。
- 如果 是正數,其表示為 。
- 如果 是負數,其表示為 。
- 如何判斷 是否代表負數?
- 最大正數的表示,例如 。
- 最小負數的表示,例如 。
- 題目說「只要一個數的最高元素為 9999 就一定是負數」。這句話是正確的,因為最大的正數 的最高元素不會是 。
- 所以,判斷方法是:
if (src[0] == 9999)則為負。 - 更嚴謹的判斷:題目說「最大正數頂多為 的一半,只到達 」。這表示最大的正數是 。
- 所以,如果
src[0] >= 5000,則為負數。 if (src[0] >= 5000):*sing = 1;(負數)- 呼叫
GetComp(src, abs)來計算絕對值,並存入abs。
else:*sing = 0;(正數)- 直接複製
src到abs。abs[i] = src[i]for 。
- 如果
- 檢查
Part 3: void add(int* a, int* b, int*c) (10%)
-
目的:將兩個大數相加。
-
解釋:
- 二補數的加法可以直接進行,不論運算元的符號。溢位會自動處理。
- 我們需要從最低位 (
c[3]) 開始逐個元素相加,並處理進位。
-
演算法:
- 初始化進位
carry = 0。 - 從最低位開始 (i = 3 down to 0):
- 計算
sum = a[i] + b[i] + carry。 c[i] = sum % 10000(取餘數作為當前位的值)。carry = sum / 10000(取整數部分作為進位)。
- 計算
- 最後,最高位的進位
carry(來自c[0]的計算) 會被忽略,這符合二補數的溢位規則。
- 初始化進位
-
範例:
- ()
c陣列。i = 3:sum = a[3] + b[3] + carry = 99 + 9901 + 0 = 10000。c[3] = 10000 % 10000 = 0。carry = 10000 / 10000 = 1。
i = 2:sum = a[2] + b[2] + carry = 0 + 9999 + 1 = 10000。c[2] = 10000 % 10000 = 0。carry = 10000 / 10000 = 1。
i = 1:sum = a[1] + b[1] + carry = 0 + 9999 + 1 = 10000。c[1] = 10000 % 10000 = 0。carry = 10000 / 10000 = 1。
i = 0:sum = a[0] + b[0] + carry = 0 + 9999 + 1 = 10000。c[0] = 10000 % 10000 = 0。carry = 10000 / 10000 = 1。
- 最終
c = {0, 0, 0, 0}。最高位的進位1被忽略。結果正確。
Part 4: void subtract(int* a, int* b, int* c) (5%)
-
目的:將兩個大數相減 ()。
-
解釋:
- 減法 可以轉換為加法 。
- 我們需要先計算 的二補數表示,然後再將 和 相加。
- 計算 的二補數,我們可以使用
GetComp函式。
-
演算法:
- 創建一個臨時陣列
neg_b[4]來儲存 的二補數表示。 - 呼叫
GetComp(b, neg_b)來計算 。 - 呼叫
add(a, neg_b, c)來計算 ,結果存入c。
- 創建一個臨時陣列
程式碼實現細節:
- 確保所有陣列操作都在 的索引範圍內。
- 處理進位時,要確保
carry是整數。 src,comp,sing,abs,a,b,c都是指向 4 個元素的整數陣列的指標。
程式碼範例 (C/C++)
第 3 題30 分
(基本 IO、邏輯規劃、空間規劃、記憶體使用) 擬以列印的方式在螢幕上呈現時間函數圖 , , , 橫軸為 , 縱軸為 (參考本題範例)。程式啟動後 a) 先詢問並輸入繪圖欲使用的空間大小 (自行選擇, 但不可與範例相同): 縱向幾行字 (建議使用奇數)? 橫向每個函數週期使用幾個英文字母 (建議使用偶數)? b) 規劃字母串矩陣來儲存繪圖所需用的字母, 先初始化成空白字母。 c) 橫軸從 處開始, 在此畫上垂直的縱軸。即在垂直的第一欄字母位置上填上 "I" 來顯示縱軸。並在其 處分別填上字母 做為函數值的標示。 d) 在與縱軸相交於 處的水平列上填上 "-" 來表示時間的橫軸。並在 處分別用字母 標示每個週期起點時間值的位置。 e) 將橫軸每個字母對應的時間的函數值在縱的位置上用 "x" 標示。 f) 將各行字串依次輸出到螢幕上。
範例:
123456789a123456789b123456789c1;
0
1 +
2 x X
X XX
X
X
X X
X
;
123456789ab0
9--x----x----x----x----x--0
X
X
X
X
XX
X
XX
X
123456789a123456789b123456789c1;
Ans: 需要繳交的資料: { A) 流程圖、B) 本題範例所使用的輸入值為何: 縱向幾行字、橫向每個函數週期使用幾個英文字母、為了測試你的程式,你會選擇怎樣的輸入值、C) 使用字母串的規劃圖、計算需要放標記的字母的位置所用到的公式、用你選用的測試輸入值代入後,會得到的縱軸與橫軸的字母位置、及標示其值的位置的數值為何, D) 程式碼。} (30%)。
登入後即可作答並保存紀錄。
本題考驗程式設計中關於圖形繪製、座標轉換、陣列操作、使用者輸入處理、以及數學函數計算的綜合能力。核心是將一個連續的數學函數 離散化,並在一個由字元組成的二維網格(類似於文字模式的繪圖)上顯示出來。
核心概念與步驟分解:
-
使用者輸入與參數設定 (a):
- 程式需要詢問使用者輸入繪圖的「空間大小」,即網格的尺寸。
vertical_lines(縱向幾行字):代表網格的行數(高度)。建議為奇數,這有助於中心線的對齊。horizontal_chars_per_period(橫向每個函數週期使用幾個英文字母):代表一個函數週期(這裡 的週期是 個時間單位)在橫軸上佔據的字元數。建議為偶數。- 程式需要儲存這些輸入值,並確保它們與範例不同。
-
網格初始化 (b):
- 建立一個二維字元陣列(或類似結構)來代表繪圖的畫布。
- 尺寸為
vertical_lines行,total_horizontal_chars列。total_horizontal_chars需要根據horizontal_chars_per_period和時間範圍 來計算。 - 一個週期是 。時間範圍 到 涵蓋了 個週期。
horizontal_chars_per_period是指一個週期佔據的字元數。- 所以,總橫軸字元數
total_horizontal_chars應與時間範圍和horizontal_chars_per_period成比例。 - 例如,如果 是 1 個週期,佔據
horizontal_chars_per_period個字元,那麼 佔據 個字元。 - 將所有網格單元初始化為空白字元(例如
' ')。
-
繪製座標軸 (c, d):
-
縱軸:
- 縱軸通常位於網格的中心行(如果
vertical_lines是奇數,中心行索引為vertical_lines / 2)。 - 在縱軸的垂直線上,第一個字元位置(最左邊)標示為
'I'。 - 同時,在縱軸上標示 的範圍:
- 最高點 () 處,標示為
'+'。 - 最低點 () 處,標示為
'-'。 - 中間點 () 處,標示為
'0'。 - 其他縱軸位置根據
vertical_lines的設定來標示。
- 最高點 () 處,標示為
- 縱軸通常位於網格的中心行(如果
-
橫軸:
- 橫軸位於 的水平線上。
- 在 的線上,標示時間點 。
- 這些時間點在網格中的橫座標位置需要計算。
- 例如,如果 處對應網格的第 0 列,那麼 處對應的列是 。
- 題目範例中,時間點 的標示點似乎是固定的,而不是隨
horizontal_chars_per_period變動。 - 範例中,橫軸用
'-'表示,時間點 用對應的數字標示。 9--x----x----x----x--0這行是橫軸。0在最右邊,9在最左邊,1, 2, 3, 4, 5標示在中間。- 這部分與「橫向每個函數週期使用幾個英文字母」的關聯性需要仔細分析。
- 範例的橫軸長度是 個字元(包括左邊的
123456789a123456789b123456789c1;)。 - 範例的縱軸高度是 行(從
0到9)。 - 範例的 標記:
- 可能對應最左邊的
0。 - 對應第一個
x。 - 對應第二個
x。 - 對應第三個
x。 - 對應第四個
x。 - 對應最右邊的
0。
- 可能對應最左邊的
- 顯然,時間 的範圍是 。
- 「橫向每個函數週期使用幾個英文字母」:週期是 。如果 是 1 個週期,佔據 個字元。那麼 佔據 個字元。
- 在範例中,
123456789a123456789b123456789c1;這串長度是 。 - 如果 總共佔據 個字元寬度。
- 那麼 的位置是 。 的位置是 。
- 的位置是 。 是 。
- 範例的橫軸
9--x----x----x----x--0長度是 個字元。 - 如果 是
9(左邊第一個), 是0(右邊第一個)。 - 那麼 的位置在哪裡?
- 標示 的點是
9,x,x,x,x,0。 - 這表示 對應
9, 對應第一個x,..., 對應0。 - 這段橫軸的寬度是 。
- 時間 範圍是 。
horizontal_chars_per_period的意義需要再釐清。
-
重新解讀:「橫向每個函數週期使用幾個英文字母」。一個週期是 。
- 假設
horizontal_chars_per_period = K。 - 那麼 佔據 個字元。
- 佔據 個字元。
- 佔據 個字元。
- 總寬度 。
- 在範例中,橫軸長度是 。所以 => 。這不是整數。
- 另一種解釋:
horizontal_chars_per_period是指 半個週期 的寬度。- 如果 (半個週期) 佔據 個字元。
- 那麼 (一個週期) 佔據 個字元。
- (2.5 個週期) 佔據 個字元。
- 範例寬度 。 => 。還是非整數。
- 假設
-
再思考:範例的橫軸標示是
9--x----x----x----x--0。9是 的標記。0是 的標記。x標記了 的位置。--是分隔符。- 總寬度 。
- 時間範圍 。
- 每個時間單位在橫軸上的像素數:
pixels_per_time_unit = W / T = 21 / 5 = 4.2。 - 這表示每個時間單位(例如從 到 )在橫軸上佔據 個字元。
horizontal_chars_per_period(建議偶數):如果它代表一個週期 ( 個時間單位) 的字元數。- 如果 是週期寬度,那麼 。
- 如果 (偶數),則 。
- 如果 (偶數),則 。
- 範例的 似乎是個特殊值。
- 重要提示:題目要求「不可與範例相同」。這表示我們輸入的
vertical_lines和horizontal_chars_per_period必須與範例不同。
-
縱軸標示:
- 縱軸範圍是 。
vertical_lines是網格的行數。- 假設
vertical_lines = H。 - 中心行索引
center_row = H / 2。 - 對應的行索引
row_for_y_1。 - 對應的行索引
row_for_y_neg_1。 - 對應的行索引
row_for_y_0。 - 公式:。
- 或者,反過來:
row_index = center_row + y_value / scale_factor。 - 我們需要確定
scale_factor。 - 從 到 的總範圍是 。
- 如果
vertical_lines = H,那麼從最底行到最頂行的 值變化範圍是 。 scale_factor應與vertical_lines的奇偶性有關。- 如果 是奇數,例如 。中心行是 。
- 標記在頂部(或接近頂部)。 標記在底部。
- 範例 (偶數),中心行可能不是單一行。
- 範例 標記在第 1 行 (索引 1), 標記在第 9 行 (索引 9)。 標記在第 9 行。
- 這與 的範圍不符。
- 重新解讀範例:
- 左邊的數字 是 軸的標示。
+在頂部,-在底部。0在中間。1 +(第 1 行)2 x X(第 2 行)- ...
9--x----x----x----x--0(第 9 行,這裡的9似乎是 軸標示,0是 的標示)。- 橫軸 在第 9 行。
- 縱軸 在第 0 列。
- 這表示 軸的範圍是從 到 (共 10 行)。
- 這與題目的 不符。
- 重要的假設:範例中的數字 和符號
+ - 0只是 標示,實際的網格是空白字元。 vertical_lines是網格的總行數。horizontal_chars_per_period是指一個週期 在橫軸上的寬度。- 時間範圍 。
total_time_span = 5。period = 2。num_periods = total_time_span / period = 5 / 2 = 2.5。total_horizontal_chars = num_periods * horizontal_chars_per_period = 2.5 * horizontal_chars_per_period。- 由於
horizontal_chars_per_period是偶數,設為 。 total_horizontal_chars = 2.5 * K。為了讓總寬度為整數, 必須是偶數,例如 。- 如果 , 。
- 如果 , 。
- 如果 , 。
- 我們需要選擇一個 (偶數),且 。
-
-
繪製函數曲線 (e):
- 遍歷橫軸上的每個字元位置
col(從 到total_horizontal_chars - 1)。 - 將網格的列座標
col轉換回時間值t。t = (col / total_horizontal_chars) * total_time_span。t = (col / (2.5 * K)) * 5 = (col * 5) / (2.5 * K) = (col * 2) / K。
- 計算對應時間
t的函數值 。 - 將 值轉換為網格的行座標
row。- 的範圍是 。
- 網格的行數是
vertical_lines(設為 )。 - 中心行索引
center_row = H / 2。 scale_factor決定了 值如何映射到行索引。- 從 到 的總範圍是 。
- 如果 是奇數,例如 。中心行是 。
- 映射到頂行(索引 0)或接近頂部的行。
- 映射到底行(索引 )或接近底部的行。
row = center_row - y / scale_factor。- 當 ,
row應最小。當 ,row應最大。 scale_factor應約等於 (如果 映射到邊緣行)。- 更簡單的映射:將 映射到行索引 。
scaled_y = (y + 1) / 2.0。這將 映射到 。row = (int)((1.0 - scaled_y) * (H - 1))。這樣 (頂行)。 (底行)。- 將計算出的
row索引處的字元設為'x'。 - 注意:如果計算出的
row超出網格範圍 ,則忽略。
- 遍歷橫軸上的每個字元位置
-
輸出網格 (f):
- 遍歷網格的每一行。
- 將每一行的字元串輸出到螢幕。
- 在輸出前,加上行號(如範例中的 )。
座標轉換與標記位置公式推導:
- 輸入參數:
H(vertical_lines):總行數。K(horizontal_chars_per_period):一個週期 () 的橫軸寬度 (必須是偶數)。
- 計算參數:
total_horizontal_chars = (int)(2.5 * K)。center_row = H / 2。total_time_span = 5.0。
- 繪製步驟:
-
初始化網格:
char grid[H][total_horizontal_chars],全部填為' '。 -
繪製縱軸:
- 縱軸位於第 列 (
col = 0)。 grid[0][0] = 'I'。- 值的範圍是 。
- 映射到行索引:。
row = (int)((1.0 - (y + 1.0) / 2.0) * (H - 1))。- 對於 , 。
- 對於 , 。
- 對於 , 。
- 在縱軸上,根據這些行標示
+,0,-:grid[0][0] = 'I'(最高點 的位置,如果 映射到 )。grid[H-1][0] = '-'(最低點 的位置)。grid[(H-1)/2][0] = '0'( 的位置)。- 如果 是奇數,
(H-1)/2是中心行。 - 如果 是偶數,中心可能不是單一行,需要根據題目範例來決定
0,+,-的具體行。
- 縱軸位於第 列 (
-
繪製橫軸:
- 橫軸位於 的線上。
row_for_y_0 = (H-1)/2(假設 是奇數)。- 在
row_for_y_0行,遍歷col從 到total_horizontal_chars - 1。 - 如果
grid[row_for_y_0][col]是空白,則設為'-'。 - 標示時間點 。
t=0對應col = 0。t=5對應col = total_horizontal_chars - 1。t=i對應的col = (int)((double)i / total_time_span * total_horizontal_chars)。- 在這些
col位置,將grid[row_for_y_0][col]設為對應的數字字元 '0' 到 '5'。 - 注意:這裡的標示可能會覆蓋掉橫軸的
'-'。
-
繪製函數曲線:
- 遍歷
col從 到total_horizontal_chars - 1(避開縱軸)。 - 計算時間 。
- 計算 。
- 計算對應的行索引
row = (int)((1.0 - (y + 1.0) / 2.0) * (H - 1))。 - 檢查
row是否在有效範圍 。 - 如果
grid[row][col]是空白,則設為'x'。
- 遍歷
-
輸出:
- 遍歷
row從 到 。 - 輸出行號 (例如
row值)。 - 輸出該行的字串。
- 遍歷
-
範例分析與參數選擇:
-
範例輸入:
- 縱向幾行字 (H):10 (偶數)
- 橫向每個函數週期幾個英文字母 (K):未知,但推測與橫軸寬度 21 有關。
-
我們需要選擇不同的 H (建議奇數) 和 K (偶數)。
-
假設我們選擇:
H = 11(奇數)。K = 10(偶數)。
-
計算:
total_horizontal_chars = 2.5 * K = 2.5 * 10 = 25。center_row = H / 2 = 11 / 2 = 5。total_time_span = 5.0。
-
繪製縱軸 (H=11, col=0):
grid[0][0] = 'I'(最高點 )grid[10][0] = '-'(最低點 )grid[5][0] = '0'()- 其他位置是空白。
-
繪製橫軸 (row=5, col=0 to 24):
grid[5][col] = '-'forcol = 0to24。- 時間點標示:
- 。
grid[5][0] = '0'。 - 。
grid[5][5] = '1'。 - 。
grid[5][10] = '2'。 - 。
grid[5][15] = '3'。 - 。
grid[5][20] = '4'。 - (超出範圍,應是 24)。
- 修正:
t=5對應col = total_horizontal_chars - 1 = 24。grid[5][24] = '5'。
- 。
-
繪製函數曲線:
- 遍歷
col從 到 。 - 計算 。
- 計算 。
- 計算
row = (int)((1.0 - (y + 1.0) / 2.0) * (11 - 1)) = (int)((1.0 - (y + 1.0) / 2.0) * 10)。 - 如果
grid[row][col]是' ',則設為'x'。
- 遍歷
公式總結:
total_horizontal_chars = (int)(2.5 * K)(其中 K 是使用者輸入的偶數,且 範例中的 K)。center_row = H / 2(其中 H 是使用者輸入的奇數)。- 時間 映射到列
col:col = (int)((t / 5.0) * total_horizontal_chars)。 - 列
col映射到時間 :t = (double)col / total_horizontal_chars * 5.0。 - 值映射到行
row:row = (int)((1.0 - (y + 1.0) / 2.0) * (H - 1))。 - 函數值 :。
需要繳交的資料 (Ans):
A) 流程圖:描述程式的整體邏輯流程,包括輸入、初始化、繪製座標軸、繪製曲線、輸出。
B) 輸入值:
* 縱向幾行字 (H):例如 11。
* 橫向每個函數週期幾個英文字母 (K):例如 10。
* 測試輸入值 (H, K):需要自行選擇一組與範例不同的 H (奇數) 和 K (偶數)。
C) 規劃圖與公式:
第 4 題20 分
- (基本搜尋的迴圈) 假設有一個系統,其中有兩個可替換調整的元件,它們的特性參數分別是 A、B。這個系統的效能受 A、B 影響的結果是我們不知道的函數 f(A,B)。我們只好在可選用的 A、B 範圍內做完整的排列組合的測試,以便找到能達成最大的及最小的 f(A,B) 的 (A,B) 組合。請寫一個程式來輔導測試的進行:
a) 程式開始,應該要求輸入 A、B 可供選擇的數目的数目及其數值清單。
b) 依完整的排列組合順序依次下達指令:[採用哪組 A 值=___ 及 B 值=_ 做測試,請將測試結果回報 f(A,B)=?],
c) 在實驗完全執行完畢後,產生分析報告: [.* 最大結果值為 f(A=, B=) = __, .* 最小結果值為 f(A=, B=_____) = ______。]。(20%)。
Ans: 需要繳交的資料: {A) 流程圖, B) 程式碼, C) 考生請將自己設想的 f(A,B) 值填入下列的測試數據表做為程式輸入的依據, D) 應該要餵給程式輸入為何。}。
| A \ B | 5 | 10 | 20 | 25 | 35 |
|---|---|---|---|---|---|
| 5 | |||||
| 10 | |||||
| 20 |
登入後即可作答並保存紀錄。
本題考驗程式設計中關於迴圈、使用者輸入、陣列處理、以及尋找最大最小值等基本概念。核心是實現一個「窮舉搜尋」(Exhaustive Search) 或「暴力搜尋」(Brute-force Search) 的演算法,來測試所有可能的 A 和 B 的組合,並找出導致系統效能最大值和最小值的那一組 (A, B)。
核心概念與步驟分解:
-
輸入參數 (a):
- 程式需要獲取 A 和 B 的「數值清單」。
- 這意味著使用者需要輸入 A 的可能值的數量,然後輸入 A 的具體數值。
- 同樣地,輸入 B 的可能值的數量,然後輸入 B 的具體數值。
- 這可以透過兩個陣列或向量來儲存 A 和 B 的數值清單。
-
排列組合測試 (b):
- 需要遍歷 A 的所有可能值,對於每一個 A 值,再遍歷 B 的所有可能值。這是一個雙重迴圈的結構。
- 對於每一組 (A, B) 的組合,程式需要「下達指令」並「回報測試結果」。
- 「下達指令」可以理解為輸出提示訊息,告知使用者正在測試哪一組 (A, B)。
- 「回報測試結果」意味著程式需要有一個方式來獲取 的值。在實際程式中,這可能是一個函式呼叫。在這裡,我們需要模擬這個過程,例如,可以讓使用者在測試時輸入 的值,或者預先定義一個簡單的 函式來產生測試值。
- 在遍歷過程中,需要記錄下目前為止找到的最大 值及其對應的 (A, B) 組合,以及最小 值及其對應的 (A, B) 組合。
-
記錄最大最小值:
- 初始化
max_f = -infinity(或一個非常小的數),min_f = +infinity(或一個非常大的數)。 - 初始化儲存最大最小值的 (A, B) 組合的變數,例如
max_A,max_B,min_A,min_B。 - 在每次測試一組 (A, B) 並獲得 值後:
- 如果 ,則更新 , , 。
- 如果 ,則更新 , , 。
- 初始化
-
產生分析報告 (c):
- 在所有 (A, B) 組合都測試完畢後,輸出最終的報告。
- 報告應包含:
- 最大值:。
- 最小值:。
程式設計細節:
-
資料結構:
- 使用
std::vector<double>(或std::vector<int>,取決於 A, B 的類型) 來儲存 A 和 B 的數值清單。 - 使用
double或int來儲存 的值,以及最大最小值。
- 使用
-
使用者輸入:
- 需要提示使用者輸入 A 的數量,然後輸入 A 的值。
- 同理,輸入 B 的數量,然後輸入 B 的值。
- 需要處理輸入錯誤的可能性(例如輸入非數字、數量為負等),但題目要求中未明確說明,可以先假設輸入是有效的。
-
模擬 f(A,B):
- 由於題目沒有給定 的具體函數,我們需要在程式中預設一個。
- 一個簡單的例子可以是 或 。
- 或者,更貼近題目要求,可以在測試時提示使用者輸入 的值。
- 題目中的表格提示,A 和 B 的值可以是 。這可以作為預設的輸入值。
-
輸出格式:
- 按照題目要求的格式輸出測試過程中的訊息和最終報告。
需要繳交的資料 (Ans):
A) 流程圖:描述程式的整體邏輯,包括輸入、迴圈、計算、記錄、輸出。
B) 程式碼:實現上述功能的程式。
C) 測試數據表:考生需要自行設計一個 函數,並將 A 和 B 的值(例如從題目表格中選取)填入表格,計算出對應的 值。
D) 餵給程式的輸入:根據考生設計的 和測試數據表,列出程式需要接收的使用者輸入。
範例 f(A,B) 函數設計 (用於程式開發):
假設我們設計一個簡單的函數,例如 。
或者,為了更貼近題意,我們可以讓它產生一些波動,例如 .
對於題目提供的表格,如果我們使用 A, B 的值為 。
考生自行設計 f(A,B) 的考量:
- 選擇一個函數,使其在給定的 A, B 值範圍內,能夠產生明顯的最大值和最小值。
- 函數的輸出類型應與程式設計時的變數類型一致(例如 double 或 int)。
範例 C) 測試數據表與 f(A,B) 設計
假設考生設計的函數是 。
使用表格中的 A 和 B 值:, (為了簡化,先取部分值)。
| A \ B | 5 | 10 | 20 |
|---|---|---|---|
| 5 | |||
| 10 | |||
| 20 |
在上面的例子中:
- 最大值是 ,對應 。
- 最小值是 ,對應 。
D) 程式輸入:
假設程式要求輸入 A 的數量和值,B 的數量和值。
- 輸入 A 的數量:3
- 輸入 A 的值:5 10 20
- 輸入 B 的數量:3
- 輸入 B 的值:5 10 20
- (如果程式模擬 f(A,B) 是由使用者輸入,則在每次測試時,程式會提示 "Enter f(A,B) value for A=..., B=...:",使用者輸入對應的值,例如 0, 50, 150, -25, 50, 200, -75, 50, 300)。
程式碼骨架 (C++)