111 經濟部聯招(台電、中油、台水、台糖)新進職員 資訊 國營事業聯招 (資訊管理、程式設計)B 考古題詳解
第 一 題15 分
一、供應商及其所銷售產品的關聯式資料庫(Relation Database),包含下列 2 個資料表(table),有底線為主鍵(Primary Key):
供應商(Suppliers)資料表 | <u>供應商代號(SupplierID)</u> | 供應商名稱(SupplierName) | 城市名稱(City) |
產品(Products)資料表 | <u>產品代號(ProductID)</u> | 產品名稱(ProductName) | 供應商代號(SupplierID) | 價格(Price) |
針對下列問題,請分別寫出 SQL 指令以進行查詢。(3 題,共 15 分) (一) 列出供應商名稱及其所銷售產品名稱,其中供應商代號從小到大排序,產品代號從大到小排序。(3 分) (二) 使用 SQL 萬用字元(Wildcards)列出供應商名稱及城市名稱,其城市名稱第 2 個字母包含 a, e, i。(5 分) (三) 使用 SQL EXISTS 語法列出產品價格 的供應商名稱。(7 分)
登入後即可作答並保存紀錄。
核心觀念
本題考查關聯式資料庫的基本查詢,包括:
- 使用
JOIN合併供應商與產品資料。 - 使用
ORDER BY進行多欄位排序。 - 使用
LIKE與萬用字元進行模糊查詢。 - 使用
EXISTS判斷是否存在符合條件的相關資料列。 - 透過主鍵
SupplierID與外鍵SupplierID建立兩資料表間的關聯。
資料表關係如下:
Suppliers.SupplierID:供應商資料表的主鍵。Products.ProductID:產品資料表的主鍵。Products.SupplierID:對應Suppliers.SupplierID的外鍵。
(一)列出供應商名稱及其所銷售產品名稱,並依指定順序排序
解題方法
供應商名稱位於 Suppliers,產品名稱位於 Products,因此必須透過兩表的 SupplierID 進行連接。
題目要求:
- 供應商代號由小到大:
SupplierID ASC - 產品代號由大到小:
ProductID DESC
使用 INNER JOIN 可列出確實有銷售產品的供應商及其產品。
SQL 指令
SELECT S.SupplierName,
P.ProductName
FROM Suppliers AS S
INNER JOIN Products AS P
ON S.SupplierID = P.SupplierID
ORDER BY S.SupplierID ASC,
P.ProductID DESC;
解題技巧
ORDER BY 的排序優先順序由左至右:
- 先依
SupplierID排序。 - 同一供應商內,再依
ProductID由大到小排序。
常見錯誤是只寫一個排序欄位,或將 ProductID 誤寫成升冪排序。
(二)使用 SQL 萬用字元列出供應商名稱及城市名稱,其城市名稱第 2 個字母為 a、e、i 之一
核心觀念
SQL LIKE 常用萬用字元如下:
%:代表零個或多個任意字元。_:代表恰好一個任意字元。
題目要求城市名稱的第 2 個字母為 a、e 或 i:
- 第 1 個字元:任意字元,因此使用
_。 - 第 2 個字元:必須是
a、e或i。 - 第 3 個字元以後:任意字元,因此使用
%。
為了確保 SQL 語法具備較佳通用性,可分別使用三個 LIKE 條件。
SQL 指令
SELECT SupplierName,
City
FROM Suppliers
WHERE City LIKE '_a%'
OR City LIKE '_e%'
OR City LIKE '_i%';
SQL Server 的簡寫寫法
若資料庫系統為 SQL Server,可使用字元集合 [aei]:
SELECT SupplierName,
City
FROM Suppliers
WHERE City LIKE '_[aei]%';
解題技巧
例如:
Taipei:第 2 個字元為a,符合。Keelung:第 2 個字元為e,符合。Hsinchu:第 2 個字元為s,不符合。
第 二 題15 分
二、目前機器學習實務上主要應用的學習方式為監督式學習及非監督式學習,請分別說明其定義、主要處理問題及常見的演算法。(15 分)
登入後即可作答並保存紀錄。
核心觀念
本題考查機器學習兩種主要學習方式:
- 監督式學習(Supervised Learning):利用具有正確答案的訓練資料,學習輸入資料與輸出答案之間的關係。
- 非監督式學習(Unsupervised Learning):利用沒有標註答案的資料,從資料本身找出潛在結構、分群關係或特徵表示。
答題時應分別說明「定義」、「主要處理問題」及「常見演算法」,並搭配實際應用例子,才能完整涵蓋配分重點。
解題方法
可採用「資料是否有標籤」作為主要切入點:
- 有標籤資料:屬於監督式學習。
- 無標籤資料:屬於非監督式學習。
再依輸出結果區分監督式學習的分類與迴歸,依分析目的說明非監督式學習的分群、降維、關聯規則及異常偵測。
一、監督式學習
1. 定義
監督式學習是將輸入資料 與其對應的正確答案或標籤 一起提供給模型訓練,使模型學習函數:
訓練完成後,模型可對新的未知資料 預測其輸出結果:
其中, 代表模型透過訓練所學得的參數。
監督式學習的核心特徵是訓練資料具有明確標籤,例如:
- 電子郵件及其「垃圾郵件/正常郵件」標籤。
- 房屋資料及其實際成交價格。
- 病患檢查資料及其「患病/未患病」結果。
2. 主要處理問題
(1)分類(Classification)
分類問題的輸出是離散類別,用於判斷資料屬於哪一個類別。
例如:
- 判斷郵件是否為垃圾郵件。
- 判斷交易是否為信用卡詐欺。
- 判斷病患是否罹患某種疾病。
- 辨識圖片中的物件種類。
分類又可分為:
- 二元分類:只有兩種結果,例如是/否。
- 多類別分類:有三種以上互斥類別,例如辨識貓、狗、鳥。
- 多標籤分類:一筆資料可同時具有多個標籤。
(2)迴歸(Regression)
迴歸問題的輸出是連續數值,用於預測數值大小。
例如:
- 預測房屋價格。
- 預測明日用電量。
- 預測商品銷售量。
- 預測設備故障前的剩餘時間。
分類與迴歸的差異在於:
- 分類輸出的是類別。
- 迴歸輸出的是連續數值。
3. 常見演算法
(1)線性迴歸(Linear Regression)
用於建立輸入變數與連續輸出值之間的線性關係,例如:
常用於房價、銷售量及需求量預測。
(2)邏輯斯迴歸(Logistic Regression)
雖然名稱含有「迴歸」,但主要用於分類,透過 Sigmoid 函數將結果轉換為機率:
其中:
當預測機率超過設定門檻時,即判定為某一類別。
(3)決策樹(Decision Tree)
利用一連串條件判斷將資料逐步分割,最後在葉節點產生分類結果或迴歸數值。
優點是容易解釋,能處理數值型及類別型特徵;缺點是樹過深時容易發生過度配適。
(4)隨機森林(Random Forest)
由多棵決策樹組成:
- 分類問題通常採多數決。
- 迴歸問題通常取各樹預測值的平均。
透過建立多棵具有差異性的樹,可降低單一決策樹過度配適的風險。
(5)支援向量機(Support Vector Machine, SVM)
尋找能夠最大化類別間隔的決策超平面。透過核心函數(Kernel Function)可處理非線性分類問題,也可延伸用於迴歸。
(6) 近鄰法(-Nearest Neighbors, KNN)
判斷新資料附近的 筆訓練資料:
- 分類時依鄰近樣本的多數類別決定結果。
- 迴歸時可取鄰近樣本輸出的平均值。
(7)類神經網路與深度學習(Neural Network and Deep Learning)
由多層神經元組成,可學習複雜的非線性關係,常應用於影像辨識、語音辨識、自然語言處理及預測問題。
二、非監督式學習
1. 定義
非監督式學習使用沒有標註答案的資料,只提供輸入資料 ,由模型自行找出資料中的內在結構、相似性及分布特徵。
其目標不是預測已知標籤,而是分析資料本身,例如:
- 哪些資料彼此相似。
- 資料可分成哪些群組。
- 原始資料中哪些特徵最重要。
- 哪些交易或行為與其他資料明顯不同。
2. 主要處理問題
(1)分群(Clustering)
依據資料之間的相似程度,將資料分成數個群組,使同一群組內的資料相似度高,不同群組之間的差異大。
例如:
- 依消費行為將顧客分群。
- 將文件依主題分類。
- 分析網路流量的行為類型。
- 將影像像素依顏色或特徵分組。
分群不需要事先知道正確類別,因此產生的群組名稱通常需要由分析人員事後解釋。
第 三 題20 分
三、依據國家資通安全防護整合服務計畫 WEB 應用程式安全參考指引,針對安全系統發展生命週期(Secure System Development Life Cycle, SSDLC),請列出下列 3 個階段類別之控制措施。(3 題,共 20 分) (一) 開發階段類別之 5 項控制措施。(10 分) (二) 測試階段類別之 2 項控制措施。(4 分) (三) 部署與維運階段類別之 3 項控制措施。(6 分)
登入後即可作答並保存紀錄。
核心觀念
本題考查安全系統發展生命週期(SSDLC)在「開發、測試、部署與維運」三階段所應採取的安全控制措施。
傳統 SDLC 主要關注功能、時程與成本;SSDLC 則將資訊安全要求納入系統生命週期,使系統在需求、設計、開發、測試、上線及維運各階段,均落實機密性、完整性與可用性(CIA)之保護。
本題以命題當年,即民國 111 年之相關法規及《WEB 應用程式安全參考指引》內容為準。現行資通系統防護基準已有文字及控制要求調整,但本題仍依民國 111 年版本作答。
解題方法
題目已明確指定三個階段及其控制措施數量,因此採「階段分類法」作答:
- 開發階段:著重安全編碼、常見漏洞、錯誤處理、源碼檢測及錯誤通報。
- 測試階段:著重以工具及攻擊模擬驗證系統安全性。
- 部署與維運階段:著重正式環境之版本控管、修補更新、服務關閉及帳密安全。
考試作答時,先寫出控制措施名稱,再補充其目的及實施方式,可同時取得列點分與說明分。
(一)開發階段類別之 5 項控制措施
1. 應針對安全需求實作必要控制措施
系統開發不得只完成業務功能,還須將前期所訂定的安全需求實際納入程式與系統設計,例如:
- 身分鑑別與授權。
- 輸入資料驗證。
- 存取權限控管。
- 機敏資料保護。
- 安全日誌與稽核機制。
重點在於將「安全需求」轉化為可執行的程式功能及系統控制措施。
2. 應注意避免軟體常見漏洞及實作必要控制措施
開發人員應依循安全編碼原則,避免常見的 Web 應用程式漏洞,例如:
- SQL Injection。
- 跨網站指令碼攻擊(XSS)。
- 不當的存取控制。
- 不安全的身分驗證。
- 不安全的組態設定。
- 敏感資料暴露。
實作上可採取參數化查詢、輸入驗證、輸出編碼、最小權限及安全的密碼處理等措施。
3. 發生錯誤時,使用者頁面僅顯示簡短錯誤訊息及代碼,不包含詳細錯誤訊息
錯誤處理須避免將系統內部資訊直接回傳給使用者,例如:
- 資料庫名稱及 SQL 語法。
- 伺服器實體路徑。
- 程式堆疊資訊。
- 函式名稱及原始碼片段。
- 作業系統版本。
使用者介面僅顯示一般性錯誤訊息及錯誤代碼;詳細錯誤內容則應記錄於受保護的系統日誌,供管理人員分析。
此措施可降低攻擊者藉由錯誤訊息取得系統結構及弱點資訊的風險。
4. 執行「源碼掃描」安全檢測
源碼掃描是以靜態分析方式檢查程式碼,不必實際執行系統,即可找出下列問題:
- 危險函式使用。
- 未驗證的輸入資料。
- 硬編碼密碼或金鑰。
- 不安全的資料庫查詢。
- 權限檢查遺漏。
- 不當的例外處理。
此項控制措施屬於開發階段的靜態安全檢測,常以 SAST(Static Application Security Testing)概念理解。
5. 具備系統嚴重錯誤之通知機制
系統發生重大錯誤、服務中斷或安全異常時,應具備即時或適當的通知機制,例如:
- 電子郵件通知。
- 簡訊通知。
- 監控平台告警。
- 資安事件管理系統通知。
通知內容應包含錯誤時間、系統名稱、錯誤等級及相關識別資訊,以利維運人員及早處理。
(二)測試階段類別之 2 項控制措施
1. 執行「弱點掃描」安全檢測
弱點掃描是使用自動化工具檢查系統、主機、網路服務及 Web 應用程式是否存在已知弱點,例如:
第 四 題15 分
四、有一費氏(Fibonacci)數學函數如下:(3 題,每題 5 分,共 15 分) (一) 請以遞迴(Recursive)方式寫出上列函式程式碼。 (二) 請以非遞迴(Non-Recursive)方式寫出上列函式程式碼。 (三) 為避免因為遞迴呼叫浪費函式重複計算的時間,試修改(一)中的程式碼,仍須使用遞迴的方式,使其計算時不須重複計算 和 函式。
登入後即可作答並保存紀錄。
核心觀念
本題考查費氏數列的遞迴定義、遞迴函式、迴圈,以及記憶化(Memoization)。
費氏數列定義為:
且:
因此前幾項為:
撰寫程式時, 與 是遞迴終止條件。
解題方法
(一)以遞迴方式撰寫
遞迴函式必須包含兩部分:
- 基底條件: 或 時直接回傳結果。
- 遞迴關係:依照題目公式計算 。
int Fibonacci(int n)
{
if (n == 0)
return 0;
if (n == 1)
return 1;
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
也可合併基底條件:
int Fibonacci(int n)
{
if (n <= 1)
return n;
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
以 Fibonacci(5) 為例:
因此程式會不斷拆解,直到遇到 或 。
此方法的時間複雜度約為:
原因是許多相同的子問題會被重複計算,例如 會在不同遞迴分支中重複出現。
(二)以非遞迴方式撰寫
非遞迴方式使用變數保存前兩項:
previous表示目前的current表示目前的next表示新的
int Fibonacci(int n)
{
if (n == 0)
return 0;
int previous = 0; // F(0)
int current = 1; // F(1)
for (int i = 2; i <= n; i++)
{
int next = previous + current;
previous = current;
current = next;
}
return current;
}
計算過程如下:
| 2 | 0 | 1 | 1 |
| 3 | 1 | 1 | 2 |
| 4 | 1 | 2 | 3 |
| 5 | 2 | 3 | 5 |
因此:
此方法只需逐項計算,每一項只計算一次,因此:
- 時間複雜度:
- 空間複雜度:
(三)使用遞迴但避免重複計算
可加入一個陣列 memo 儲存已計算過的結果:
memo[n] == -1:代表 尚未計算。memo[n] != -1:代表已有結果,直接回傳。
#define MAX_N 100
int memo[MAX_N];
int FibonacciMemo(int n)
{
if (n == 0)
return 0;
if (n == 1)
return 1;
if (memo[n] != -1)
return memo[n];
memo[n] = FibonacciMemo(n - 1) + FibonacciMemo(n - 2);
return memo[n];
}
第 五 題15 分
五、A 公司 1 月人事資料檔案如下表,請撰寫下列程式片段。(2 題,共 15 分)
| emp-id(員工代號) | dep-id(部門代號) | age(年齡) | s_salary(特別費) |
|---|---|---|---|
| 123455 | ABC | 25 | 0 |
| 987655 | ABC | 30 | 5000 |
| A12345 | AAB | 30 | 10000 |
| 123455 | AAB | 30 | 500 |
| 12345 | AXX | 25 | 5000 |
| 234561 | AXX | 35 | 15000 |
(一) A 公司員工代號共 6 碼數字,第 6 碼是前 5 碼總和的個位數,請撰寫一函式 check_dit(),以檢查員工代號之正確性。(7 分)
(二) 請撰寫一函式 main(),逐筆讀取人事資料檔案,並呼叫(一)函式,篩選正確員工代號資料,最後列出特別費合計超過 100,000 元之部門代號及合計金額。(8 分)
登入後即可作答並保存紀錄。
核心觀念
本題考查:
- 字串資料驗證
- 員工代號必須為恰好 6 碼。
- 6 碼皆須為數字。
- 第 6 碼須等於前 5 碼數字總和的個位數。
- 個位數計算
若前 5 碼為 ,第 6 碼為 ,則驗證公式為:
- 分組彙總
- 逐筆讀取資料。
- 僅保留員工代號正確的紀錄。
- 依部門代號分組,累計
s_salary。 - 最後列出合計金額大於 元的部門。
解題方法
員工代號應以字串讀取,而不宜直接以整數讀取,原因如下:
- 需要逐一檢查每個字元是否為數字。
- 若員工代號可能以
0開頭,使用整數會遺失開頭的0。 - 字串可以直接檢查長度是否為 6。
驗證流程如下:
- 判斷字串長度是否為 6。
- 檢查 6 個字元是否都是數字。
- 將前 5 碼轉成數值後加總。
- 將加總值取
% 10,與第 6 碼比較。 - 相同即為正確員工代號。
(一)check_dit 函式
以下使用 C 語言撰寫,回傳 1 表示員工代號正確,回傳 0 表示錯誤。
#include <string.h>
#include <ctype.h>
int check_dit(const char emp_id[])
{
int i;
int sum = 0;
int check_digit;
/* 員工代號必須恰好 6 碼 */
if (strlen(emp_id) != 6)
return 0;
/* 6 碼皆必須是數字 */
for (i = 0; i < 6; i++) {
if (!isdigit((unsigned char)emp_id[i]))
return 0;
}
/* 計算前 5 碼總和 */
for (i = 0; i < 5; i++) {
sum += emp_id[i] - '0';
}
/* 第 6 碼為前 5 碼總和的個位數 */
check_digit = sum % 10;
if (emp_id[5] - '0' == check_digit)
return 1;
else
return 0;
}
資料驗證
| 員工代號 | 前 5 碼總和 | 個位數 | 判定 |
|---|---|---|---|
123455 | 正確 | ||
987655 | 正確 | ||
A12345 | 含有英文字母 | — | 錯誤 |
123455 | 正確 | ||
12345 | 僅 5 碼 | — | 錯誤 |
234561 | 錯誤 |
注意:第 2 筆 987655 的前 5 碼總和為 ,個位數是 ,因此也是正確員工代號。
(二)main 函式
部門代號採用陣列儲存,讀取每筆資料時:
- 先呼叫
check_dit。 - 員工代號錯誤時跳過該筆資料。
- 員工代號正確時,尋找相同的部門代號並累加特別費。
- 若找不到該部門,新增一筆部門資料。
第 六 題20 分
六、快速排序法(Quick Sort)是排序演算中的一種,處理過程是先選擇一個資料為基準點,所有比基準點小的元素放在左邊,比基準點大的元素放在右邊,之後再反覆對基準點左右兩邊的數列執行相同的處理,直到數列只剩一個數值或沒有數值時即完成排序。請撰寫一函式 QuickSort(),將傳入的一維陣列利用快速排序法,由小至大排序陣列元素。(20 分)
登入後即可作答並保存紀錄。
核心觀念
快速排序法(Quick Sort)採用「分而治之」(Divide and Conquer):
- 選定一個基準值(pivot)。
- 重新排列資料,使:
- 基準值左側的元素不大於基準值;
- 基準值右側的元素大於基準值。
- 基準值放到排序後的正確位置。
- 對基準值左、右兩側的子陣列遞迴執行相同程序。
- 子陣列元素個數為 或 時,即視為已排序。
本題要求由小到大排序,核心是「分割」(partition)與「遞迴」(recursion)。
解題方法
採用 Lomuto partition 分割法,選擇目前區間最右側元素作為 pivot。
設目前處理範圍為 :
- pivot =
- 使用索引
i指向下一個應放置「小於或等於 pivot」元素的位置。 - 使用索引
j由left掃描到right - 1。 - 若 ,便將 與 交換,並令
i加一。 - 掃描完成後,將 pivot 與 交換。
- 此時 pivot 位於最終正確位置
i。
分割完成後,遞迴處理:
以及
pivot 不必再處理,因為它已經位於排序後的正確位置。
參考程式碼
以下以 C 語言表示:
void Swap(int *x, int *y)
{
int temp = *x;
*x = *y;
*y = temp;
}
int Partition(int A[], int left, int right)
{
int pivot = A[right];
int i = left;
int j;
for (j = left; j < right; j++)
{
if (A[j] <= pivot)
{
Swap(&A[i], &A[j]);
i++;
}
}
// 將 pivot 放到正確位置
Swap(&A[i], &A[right]);
return i;
}
void QuickSort(int A[], int left, int right)
{
int pivotIndex;
if (left >= right)
return;
pivotIndex = Partition(A, left, right);
QuickSort(A, left, pivotIndex - 1);
QuickSort(A, pivotIndex + 1, right);
}
若題目要求只傳入陣列與元素個數,可再撰寫外部介面:
void QuickSortArray(int A[], int n)
{
if (n <= 1)
return;
QuickSort(A, 0, n - 1);
}
分割程序說明
以陣列:
為例,選最右側元素 為 pivot。
初始狀態:
掃描過程:
- ,不交換。
- ,將 移至左側。
- ,不交換。
- ,將 移至左側。
最後將 pivot 放入正確位置: