Python 入門指南

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

~~學習進度表~~

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

Syntax Errors
→ compiler or interpreter
Runtime Errors
→ exception handling
Semantic Errors
→ programmer

直譯器 (interpreter) 會直接幫我們挑出語法錯誤,例如打錯識別字 (identifier) 名稱或是縮排錯誤等等。執行期間錯誤的話, Python 另有例外處理 (exception handling) 的機制,讓我們可以處理執行期間錯誤。三種錯誤中最麻煩的,就是語意錯誤了,因為有語意錯誤的程式,程式可以順利執行完畢,卻跑出錯誤的結果。

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

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

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

要知道哪些組合可能會發生錯誤,我們就得知道 ab 的值,這不難,印出來就看得到了

# 使用 randint()
import random

# 定義 Encrypt 類別 
class Encrypt:
  def __init__(self):
    self.setcode()
    
  def setcode(self):
    # 取得 a 、 b 值
    a = random.randint(0, 9)
    print(a) # 印出 a
    b = random.randint(0, 9)
    print(b) # 印出 b
    # 利用公式建立密碼表
    self.code = ""
    c = "a"
    i = 0
    while i < 26:
      x = c
      y = ord(x) * a + b
      m = y % 26
      self.code += chr(m + 97)
      c = chr(ord(c) + 1)
      i += 1
    
  def getcode(self):
    return self.code
    
  # 編碼的方法
  def toEncode(self, str):
    pass
    
  # 解碼的方法
  def toDecode(self, str):
    pass
    
# 測試部分
if __name__ == '__main__':
  e = Encrypt()
  print()
  print(e.getcode())
  print()
  
#《程式語言教學誌》的範例程式
# http://kaiching.org/
# 檔名:encrypt04.py
# 功能:示範利用 Python 設計 Encrypt 類別
# 作者:張凱慶 */

這裡在 setcode() 加入印出 ab 值的程式碼,因為 ab 是屬於 setcode() 的區域變數 (local variable) ,因此只能在 setcode() 裡使用這兩個變數 (variable) 。

來測試看看囉

$ python encrypt04.py
4
9
 
hlptxbfjnrvzdhlptxbfjnrvzd
 
$

第一次, a4b9 就出問題,再多測試幾次看看

$ python encrypt04.py
6
2
 
msyekqwciouagmsyekqwciouag
 
$ python encrypt04.py
9
0
 
pyhqzirajsbktcludmvenwfoxg
 
$ python encrypt04.py
0
8
 
iiiiiiiiiiiiiiiiiiiiiiiiii
$

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

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

這樣我們把發展中的版本 encrypt04.py 修改為 encrypt05.py ,其中需要修改的部份只有 setcode() ,如下

# 使用 randint()
import random

# 定義 Encrypt 類別 
class Encrypt:
  def __init__(self):
    self.setcode()
    
  def setcode(self):
    # 取得 a 、 b 值
    a = 0
    b = 0
    while a % 2 == 0:
      a = random.randint(0, 9)
      b = random.randint(0, 9)

    # 利用公式建立密碼表
    self.code = ""
    c = "a"
    i = 0
    while i < 26:
      x = c
      y = ord(x) * a + b
      m = y % 26
      self.code += chr(m + 97)
      c = chr(ord(c) + 1)
      i += 1
    
  def getcode(self):
    return self.code
    
  # 編碼的方法
  def toEncode(self, str):
    pass
    
  # 解碼的方法
  def toDecode(self, str):
    pass
    
# 測試部分
if __name__ == '__main__':
  e = Encrypt()
  print()
  print(e.getcode())
  print()
  
#《程式語言教學誌》的範例程式
# http://kaiching.org/
# 檔名:encrypt05.py
# 功能:示範利用 Python 設計 Encrypt 類別
# 作者:張凱慶 */

重新執行測試如下

$ python encrypt05.py
 
zejotydinsxchmrwbglqvafkpu
 
$ python encrypt05.py
 
yzabcdefghijklmnopqrstuvwx
 
$ python encrypt05.py
 
kryfmtahovcjqxelszgnubipwd
 
$ python encrypt05.py
 
tuvwxyzabcdefghijklmnopqrs
 
$

我們先把 ab 的初值設為 0 ,然後以迴圈 (loop) 取得隨機的 ab 值,用 a 除以 2 的餘數為 0 當作迴圈的執行條件,因此當 a 等於 0 或偶數時,迴圈就會持續進行,直到 a 不為 0 或偶數為止。肉眼檢查下,似乎只要 a 為奇數,計算出的結果就不會有問題囉!

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

中英文術語對照
類別class
例外處理exception handling
識別字identifier
直譯器interpreter
區域變數local variable
迴圈loop
方法method
執行期間錯誤run-time error
語意錯誤semantic error
語法錯誤syntax error
變數variable
重點整理
1. 程式中可能會發生語法錯誤、執行期間錯誤或語意錯誤,編譯器會直接挑出語法錯誤,而執行期間錯誤可用例外處理機制來防範。
2. 發生語意錯誤的程式可順利執行跑出結果,可是結果不符合預期。
3. Encrypt 的數學公式經過再三的測試檢驗,發現問題出在 a0 或偶數的情況,因此要避免 a0 或偶數。
問題與討論
1. 什麼是語法錯誤?試舉出語法錯誤的五個例子。
2. 什麼是執行期間錯誤?想一想有哪些情況會發生執行期間錯誤?又要怎麼樣防範呢?
3. 為什麼語意錯誤最麻煩呢?
4. 我們是用什麼方式找出數學公式的錯誤呢?
練習
1. 承接上一個單元的猜數字遊戲,將新程式寫在 exercise1701.py 中,在遊戲迴圈中用變數 times 計算使用者猜測的次數,並在遊戲結束時印出 times
2. 承上題,將新程式寫在 exercise1702.py 中,設計一個檢查使用者輸入是否有重複數字的 find_number() 函數,結果回傳布林值。

相關教學影片

上一頁 單元 16 - 繼續測試
回 Python 入門指南首頁
下一頁 單元 18 - 編碼
回 Python 教材首頁
回程式語言教材首頁