Java 入門指南

單元 16 - 修正後的數學公式

~~學習進度表~~

程式中可能會發生的錯誤有三種,分別是語法錯誤 (syntax error) 、執行期間錯誤 (run-time error) 及語意錯誤 (semantic error)

Syntax Errors
→ 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

其中 ab 若是 09 隨機整數,有些組合成立,可以得到正確結果,有些組合卻會得到錯誤的結果,這是為什麼呢?嗯,好麻煩唷!這樣就得討論好多數學,打斷我們發展程式的腳步,所以我們不打算仔細討論這背後的數學理論,我們繼續測試,直接來找出哪些組合會得到錯誤的結果吧!

要知道哪些組合可能會發生錯誤,我們就得知道 ab 的值,這不難,印出來就看得到了。我們把 Encrypt03 加入印出 ab 值的程式碼,修改成 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() 印出 ab 值, printf() 預設不會印出新行符號,因此印出結果會是 ab 值加上密碼表一行

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++;
    }
}

我們先把 ab 的初值設為 0 ,然後以迴圈 (loop) 取得隨機的 ab 值,用 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) 。

相關教學影片

上一頁 單元 15 - 繼續測試
回 Java 入門指南首頁
下一頁 單元 17 - 編碼與解碼
回 Java 教材首頁
回程式語言教材首頁
中英文術語對照
class類別
compiler編譯器
exception handling例外處理
identifier識別字
loop迴圈
method方法
run-time error執行期間錯誤
semantic error語意錯誤
syntax error語法錯誤
參考資料
1. The Java™ Tutorials: Declaring Classes
2. The Java™ Tutorials: Declaring Member Variables
3. The Java™ Tutorials: Defining Methods
4. The Java™ Tutorials: Providing Constructors for Your Classes
5. The Java™ Tutorials: Returning a Value from a Method
6. The Java™ Tutorials: Using the this Keyword
7. The Java™ Tutorials: Controlling Access to Members of a Class
8. The Java™ Tutorials: Understanding Class Members
9. The Java™ Tutorials: Initializing Fields
重點整理
1. 程式中可能會發生語法錯誤、執行期間錯誤或語意錯誤,編譯器會直接挑出語法錯誤,而執行期間錯誤可用例外處理機制來防範。
2. 發生語意錯誤的程式可順利執行跑出結果,可是結果不符合預期。
3. Encrypt 的數學公式經過再三的測試檢驗,發現問題出在 a0 或偶數的情況,因此要避免 a0 或偶數。
問題與討論
1. 什麼是語法錯誤?試舉出語法錯誤的五個例子。
2. 什麼是執行期間錯誤?想一想有哪些情況會發生執行期間錯誤?又要怎麼樣防範呢?
3. 為什麼語意錯誤最麻煩呢?
4. 我們是用什麼方式找出數學公式的錯誤呢?
練習
1. 承接上一個單元的猜數字遊戲,將新程式寫在 Exercise1601 專案中,在遊戲迴圈中用變數 times 計算使用者猜測的次數,並在遊戲結束時印出 times
2. 承上題,將新程式寫在 Exercise1602 專案中,設計一個檢查使用者輸入是否有重複數字的 findNumber() 方法,結果回傳布林值。