Java 入門指南 V2.00 - 單元 16 - 修正後的數學公式




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



編譯器 (compiler) 會直接幫我們挑出語法錯誤,例如打錯識別字 (identifier) 名稱或是漏打分號等等。執行期間錯誤的話, Java 另有例外處理 (exception handling) 的機制,讓我們可以處理開啟指定檔名,而檔案不存在的情況。三種錯誤中最麻煩的,就是語意錯誤了,因為有語意錯誤的程式,程式可以順利執行完畢,卻跑出錯誤的結果。


我們的 Encrypt 類別 (class) 目前正是碰到了發生語意錯誤的情況,這是說


y = a * x + b
m = y % n
r = m + diff


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


要知道哪些組合可能會發生錯誤,我們就得知道 ab 的值,這不難,印出來就看得到了。我們把 Encrypt03 加入印出 ab 值的程式碼,修改成 Encrypt04 ,如下


001public class Encrypt04 {
002   // 密碼表字元陣列
003   private char[] code = new char[26];
004
005   // 建構子
006   public Encrypt04() {
007      setCode();
008   }
009
010   // setter
011   public void setCode() {
012      int a = 0;
013      int b = 0;
014
015      a = (int) (Math.random() * 10);
016      System.out.printf("%d, ", a);
017      b = (int) (Math.random() * 10);
018      System.out.printf("%d, ", b);
019
020      int x, y, m, i;
021      char c = 'a';
022      for (i = 0; i < 26; i++) {
023         x = c;
024         y = x * a + b;
025         m = y % 26;
026         code[i] = (char) (m + 97);
027         c++;
028      }
029   }
030
031   // getter
032   public char[] getCode() {
033      return code;
034   }
035
036   // 編碼的方法
037   public String toEncode(String s) {
038      return s;
039   }
040
041   // 解碼的方法
042   public String toDecode(String s) {
043      return s;       
044   }
045
046   // 測試的 main()
047   public static void main(String[] args) {
048      for (int i = 0; i < 16; i++) {
049         Encrypt04 e = new Encrypt04();
050         System.out.println(e.getCode());
051      }
052      //String s = "There is no spoon";
053      //System.out.println(s);
054      //String s1 = e.toEncode(s);
055      //System.out.println(s1);
056      //String s2 = e.toDecode(s1);
057      //System.out.println(s2);
058   }
059}
060 
061/* 檔名: Encrypt04.java
062   作者: Kaiching Chang
063   時間: September, 2014  */

我們在 setCode() 中用 printf() 印出 ab 值, printf() 預設不會印出新行符號,因此印出結果會是 ab 值加上密碼表一行


015a = (int) (Math.random() * 10);
016System.out.printf("%d, ", a);
017b = (int) (Math.random() * 10);
018System.out.printf("%d, ", b);

來編譯測試看看囉



結果顯示 a 為偶數或 0 就會跑出不符預期的結果,那我們就把 a 改成不是偶數或 0 好了!公式修改如下


if (a % 2) != 0 {
   y = a * x + b
   m = y % n
   r = m + diff
}


這樣我們把發展中的版本 Encrypt04 修改為 Encrypt05 ,其中需要修改的部份只有 setCode() ,如下


010// setter
011public void setCode() {
012   int a = 0;
013   int b = 0;
014
015   while (a % 2 == 0) {
016      a = (int) (Math.random() * 10);
017      b = (int) (Math.random() * 10);
018   }
019   System.out.printf("%d, ", a);
020   System.out.printf("%d, ", b);
021
022   int x, y, m, i;
023   char c = 'a';
024   for (i = 0; i < 26; i++) {
025      x = c;
026      y = x * a + b;
027      m = y % 26;
028      code[i] = (char) (m + 97);
029      c++;
030   }
031}

我們先把 ab 的初值設為 0 ,然後以迴圈 (loop) 取得隨機的 ab 值,用 a 除以 2 的餘數為 0 當作迴圈的執行條件,因此當 a 等於 0 或偶數時,迴圈就會持續進行,直到 a 不為 0 或偶數為止


012int a = 0;
013int b = 0;
014
015while (a % 2 == 0) {
016   a = (int) (Math.random() * 10);
017   b = (int) (Math.random() * 10);
018}
019System.out.printf("%d, ", a);
020System.out.printf("%d, ", b);
021

重新編譯執行,結果如下



肉眼檢查下,似乎只要 a 為奇數,計算出的結果就不會有問題囉!


下面我們要開始實作處理編碼及解碼的部分,也就是實作 toEncode()toDncode() 方法 (method) 。


中英文術語對照


語法錯誤syntax error
執行期間錯誤run-time error
語意錯誤semantic error
編譯器compiler
識別字identifier
例外處理exception handling
類別class
迴圈loop
方法method

重點整理


  1. 程式中可能會發生的錯誤有三種,分別是語法錯誤、執行期間錯誤及語意錯誤。
  2. 語意錯誤是程式順利執行,卻跑出非預期結果的情況。
  3. 公式中的 ab 值在 a 為偶數的時候出錯。

問題與討論


  1. 為什麼編譯器會直接挑出語法錯誤?
  2. 什麼是執行期間錯誤?為什麼會發生執行期間錯誤?
  3. 什麼是語意錯誤?為什麼發生語意錯誤的程式還能順利執行?

the end

沒有留言: