Java 入門指南
單元 16 - 修正後的數學公式
程式中可能會發生的錯誤有三種,分別是語法錯誤 (syntax error) 、執行期間錯誤 (run-time error) 及語意錯誤 (semantic error)
→ compiler or interpreter
Runtime Errors
→ exception handling
Semantic Errors
→ programmer
編譯器 (compiler) 會直接幫我們挑出語法錯誤,例如打錯識別字 (identifier) 名稱或是漏打分號等等。執行期間錯誤的話, Java 另有例外處理 (exception handling) 的機制,讓我們可以處理開啟指定檔名,而檔案不存在的情況。三種錯誤中最麻煩的,就是語意錯誤了,因為有語意錯誤的程式,程式可以順利執行完畢,卻跑出錯誤的結果。
我們的 Encrypt 類別 (class) 目前正是碰到了發生語意錯誤的情況,這是說
y = a * x + b
m = y % n
r = m + diff
其中 a 與 b 若是 0 到 9 隨機整數,有些組合成立,可以得到正確結果,有些組合卻會得到錯誤的結果,這是為什麼呢?嗯,好麻煩唷!這樣就得討論好多數學,打斷我們發展程式的腳步,所以我們不打算仔細討論這背後的數學理論,我們繼續測試,直接來找出哪些組合會得到錯誤的結果吧!
要知道哪些組合可能會發生錯誤,我們就得知道 a 與 b 的值,這不難,印出來就看得到了。我們把 Encrypt03 加入印出 a 、 b 值的程式碼,修改成 Encrypt04 ,如下
/*
* 檔名:Encrypt04.java
* 作者:張凱慶
* 網站:http://kaiching.org
*/
package encrypt04;
public class Encrypt04 {
// 密碼表字元陣列
private char[] code = new char[26];
// 建構子
public Encrypt04() {
setCode();
}
// setter
public void setCode() {
int a = 0;
int b = 0;
a = (int) (Math.random() * 10);
System.out.printf("%d, ", a);
b = (int) (Math.random() * 10);
System.out.printf("%d, ", b);
int x, y, m, i;
char c = 'a';
for (i = 0; i < 26; i++) {
x = c;
y = x * a + b;
m = y % 26;
code[i] = (char) (m + 97);
c++;
}
}
// getter
public char[] getCode() {
return code;
}
// 編碼的方法
public String toEncode(String s) {
return s;
}
// 解碼的方法
public String toDecode(String s) {
return s;
}
// 測試的 main()
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
Encrypt04 e = new Encrypt04();
System.out.println(e.getCode());
}
//String s = "There is no spoon.";
//System.out.println(s);
//String s1 = e.toEncode(s);
//System.out.println(s1);
//String s2 = e.toDecode(s1);
//System.out.println(s2);
}
}
我們在 setCode() 中用 printf() 印出 a 、 b 值, printf() 預設不會印出新行符號,因此印出結果會是 a 、 b 值加上密碼表一行
a = (int) (Math.random() * 10);
System.out.printf("%d, ", a);
b = (int) (Math.random() * 10);
System.out.printf("%d, ", b);
來測試看看囉
0, 9, jjjjjjjjjjjjjjjjjjjjjjjjjj |
1, 9, cdefghijklmnopqrstuvwxyzab |
4, 8, gkoswaeimquycgkoswaeimquyc |
3, 6, loruxadgjmpsvybehknqtwzcfi |
1, 4, xyzabcdefghijklmnopqrstuvw |
4, 6, eimquycgkoswaeimquycgkoswa |
0, 8, iiiiiiiiiiiiiiiiiiiiiiiiii |
0, 5, ffffffffffffffffffffffffff |
2, 0, moqsuwyacegikmoqsuwyacegik |
9, 6, venwfoxgpyhqzirajsbktcludm |
5, 7, ydinsxchmrwbglqvafkpuzejot |
5, 5, wbglqvafkpuzejotydinsxchmr |
0, 1, bbbbbbbbbbbbbbbbbbbbbbbbbb |
1, 3, wxyzabcdefghijklmnopqrstuv |
8, 8, emucksaiqygowemucksaiqygow |
8, 6, cksaiqygowemucksaiqygowemu |
2, 2, oqsuwyacegikmoqsuwyacegikm |
4, 0, ycgkoswaeimquycgkoswaeimqu |
3, 9, oruxadgjmpsvybehknqtwzcfil |
7, 9, mtahovcjqxelszgnubipwdkryf |
結果顯示 a 為偶數或 0 就會跑出不符預期的結果,那我們就把 a 改成不是偶數或 0 好了!公式修改如下
if (a % 2) != 0 { y = a * x + b m = y % n r = m + diff }
這樣我們把發展中的版本 Encrypt04 修改為 Encrypt05 ,其中需要修改的部份只有 setCode() ,如下
// setter
public void setCode() {
int a = 0;
int b = 0;
while (a % 2 == 0) {
a = (int) (Math.random() * 10);
b = (int) (Math.random() * 10);
}
System.out.printf("%d, ", a);
System.out.printf("%d, ", b);
int x, y, m, i;
char c = 'a';
for (i = 0; i < 26; i++) {
x = c;
y = x * a + b;
m = y % 26;
code[i] = (char) (m + 97);
c++;
}
}
我們先把 a 與 b 的初值設為 0 ,然後以迴圈 (loop) 取得隨機的 a 、 b 值,用 a 除以 2 的餘數為 0 當作迴圈的執行條件,因此當 a 等於 0 或偶數時,迴圈就會持續進行,直到 a 不為 0 或偶數為止
int a = 0;
int b = 0;
while (a % 2 == 0) {
a = (int) (Math.random() * 10);
b = (int) (Math.random() * 10);
}
System.out.printf("%d, ", a);
System.out.printf("%d, ", b);
執行結果如下
3, 7, mpsvybehknqtwzcfiloruxadgj |
9, 6, venwfoxgpyhqzirajsbktcludm |
7, 8, lszgnubipwdkryfmtahovcjqxe |
7, 0, dkryfmtahovcjqxelszgnubipw |
7, 1, elszgnubipwdkryfmtahovcjqx |
5, 8, zejotydinsxchmrwbglqvafkpu |
5, 5, wbglqvafkpuzejotydinsxchmr |
5, 8, zejotydinsxchmrwbglqvafkpu |
7, 4, hovcjqxelszgnubipwdkryfmta |
5, 5, wbglqvafkpuzejotydinsxchmr |
3, 8, nqtwzcfiloruxadgjmpsvybehk |
9, 0, pyhqzirajsbktcludmvenwfoxg |
5, 5, wbglqvafkpuzejotydinsxchmr |
9, 1, qzirajsbktcludmvenwfoxgpyh |
7, 3, gnubipwdkryfmtahovcjqxelsz |
3, 5, knqtwzcfiloruxadgjmpsvybeh |
5, 9, afkpuzejotydinsxchmrwbglqv |
7, 6, jqxelszgnubipwdkryfmtahovc |
1, 8, bcdefghijklmnopqrstuvwxyza |
7, 5, ipwdkryfmtahovcjqxelszgnub |
肉眼檢查下,似乎只要 a 為奇數,計算出的結果就不會有問題囉!
下面我們要開始實作處理編碼的部分,也就是實作 toEncode() 方法 (method) 。
相關教學影片
中英文術語對照 | |
---|---|
class | 類別 |
compiler | 編譯器 |
exception handling | 例外處理 |
identifier | 識別字 |
loop | 迴圈 |
method | 方法 |
run-time error | 執行期間錯誤 |
semantic error | 語意錯誤 |
syntax error | 語法錯誤 |
重點整理 |
---|
1. 程式中可能會發生語法錯誤、執行期間錯誤或語意錯誤,編譯器會直接挑出語法錯誤,而執行期間錯誤可用例外處理機制來防範。 |
2. 發生語意錯誤的程式可順利執行跑出結果,可是結果不符合預期。 |
3. Encrypt 的數學公式經過再三的測試檢驗,發現問題出在 a 為 0 或偶數的情況,因此要避免 a 為 0 或偶數。 |
問題與討論 |
---|
1. 什麼是語法錯誤?試舉出語法錯誤的五個例子。 |
2. 什麼是執行期間錯誤?想一想有哪些情況會發生執行期間錯誤?又要怎麼樣防範呢? |
3. 為什麼語意錯誤最麻煩呢? |
4. 我們是用什麼方式找出數學公式的錯誤呢? |
練習 |
---|
1. 承接上一個單元的猜數字遊戲,將新程式寫在 Exercise1601 專案中,在遊戲迴圈中用變數 times 計算使用者猜測的次數,並在遊戲結束時印出 times 。 |
2. 承上題,將新程式寫在 Exercise1602 專案中,設計一個檢查使用者輸入是否有重複數字的 findNumber() 方法,結果回傳布林值。 |