同一個問題在不同制度裡會得到不同的算式:有的加倍、有的遞增加權、有的要求先把字母換成數字。這些做法其實同屬一個家族,骨架完全一樣。本文把校驗位演算法當成一個家族來比較,說明 mod-10、mod-11 與 mod-97 三支主要成員的差異,以及挑選時真正該問的四個問題。
校驗位演算法家族有哪幾支?
把所有成員拆開來看,它們共用同一個骨架:把前面的位依某組權重加權求和,對一個模數取餘數,再把餘數(或它的補數)映射成一位字元,附在號碼尾端。家族成員之間的差別,全部集中在三個旋鈕上。
第一個旋鈕是模數。模 10 與模 11 是最常見的兩種選擇,長號碼則常改用模 97。模數愈大,可能的餘數愈多,能區分的錯誤型態也愈細。第二個旋鈕是權重序列,可以是固定值、可以是遞增或遞減的序列,也可以是交替的兩三個值。第三個旋鈕是映射方式:餘數直接當校驗位、用模數減去餘數當校驗位,或者把特定的餘數對應到字母。
三支主要成員大致可以這樣區分:模 10 家族最常見,其中一支會對每隔一位的數字加倍再求和;模 11 家族由國際標準規範出多個變體,涵蓋不同長度與字元集;模 97 家族則因為模數夠大,特別適合長度長、又允許字母的號碼。
| 分支 | 模數 | 常見場合 | 餘數的處理 |
|---|---|---|---|
| mod-10 | 10 | 純數字、長度固定 | 取補數或直接取值 |
| mod-11 | 11 | 純數字或含字母 | 可能需要額外映射,例如以字母表示 |
| mod-97 | 97 | 長號碼、字母數字混合 | 取補數後以兩位呈現 |
mod-10、mod-11 與 mod-97 的差別在哪裡
模 10 家族的優點是算得快、不需要查表,而且對人工輸入的單一字元錯誤幾乎都能抓到;缺點是權重對稱時,相鄰兩位互換的總和不變,因而漏掉那類錯誤。這也是為什麼家族裡會出現「每隔一位加倍」這種非對稱的變體。
模 11 家族的優點是模數比較大,能區分的錯誤型態更多,適用於長度不一或含字母的號碼。代價是餘數可能出現兩位數,必須額外規定怎麼表示:有的體系選擇改用字母,有的則調整權重讓餘數落在可表示的範圍內。這是選用時最先遇到的現實約束。
模 97 家族的特點是把整串號碼(包含字母,經固定規則轉換後)一起納入計算。因為模數夠大,它可以同時容納長度較長與字元集較寬的號碼,而仍然只產生一個餘數。代價是運算量比前兩者大,而且一旦字元集或長度改變,參數就得跟著重新確認。
加權與取模:家族內部的參數怎麼變
拆到這一層就會發現,所謂「不同演算法」往往只是同一支骨架換了幾個參數。權重可以是全 1、可以是遞增的自然數列、可以是週期性交替的兩個值,也可以是預先定義好的一串固定數字。
取模之後的處理同樣有變化空間。最單純的是直接把餘數當校驗位;第二種是用模數減去餘數,好讓總和能被模數整除;第三種則是在餘數等於某個特定值時改用另一個符號,這也是某些體系會出現字母校驗位的原因。
知道參數在哪裡,就能理解為什麼跨制度時不能自己發明規則。你以為「差不多」的權重差一格,算出來的校驗位就完全不同。正確的做法是照該制度公布的方式實作,並用該制度自己公布的樣本自檢,而不是憑家族的相似性去猜。
為什麼有的演算法抓得住換位錯誤?
單一字元被抄錯,幾乎每一支都能抓到,因為任何一個位變動都會改變加權總和。相鄰兩位互換就沒那麼簡單了:如果那兩位的權重相同,交換前後的和一模一樣,整串算出來的餘數當然也相同,錯誤就這樣被放過。
所以抓不抓得住換位,關鍵在於權重是不是非對稱的。當相鄰兩個位置的權重不同,交換後加權總和的變化量通常不是模數的倍數,餘數就會改變,校驗位自然對不上。這也是許多制度刻意採用交替權重的原因。
反過來說,相隔較遠的換位、整段被貼錯、以及兩個錯誤恰好互相抵銷,本來就不是一位校驗位能處理的範圍。把它的能力說成「能抓出所有錯誤」是誇大,實際的定位只是擋掉最常見的抄錄疏失。
選演算法時真正要問的四件事
在決定要用哪一支之前,先把這四個問題想清楚,通常比直接抄一份實作省事:
- 號碼有多長?長度上限決定了模數的選擇空間。
- 字元集是純數字,還是允許字母?允許字母就得處理字母轉數字的規則。
- 錯誤主要來自人工輸入,還是機器產生?人工輸入要優先考慮換位錯誤。
- 校驗位算出來可能是兩位數時,要怎麼表示?這一點必須先跟上下游談定。
這四個答案會把選項快速收斂到一兩支,剩下的就是實作細節。若你面對的是卡號那一類的既有體系,它的規則與手算過程屬於另一條線,可以讀Luhn 演算法怎麼算;本文只把它當成家族裡的一個成員看待。
給開發者:同一個家族帶來的實作統一
既然成員共用骨架,實作時就不該為每一支各寫一份。比較好的切法是把它做成三層可組合的設定:
- 前置轉換:負責把輸入轉成統一的數值序列,包含字母轉數字與大小寫處理。
- 參數描述:權重、模數與映射方式用資料描述,而不是散在流程判斷裡。
- 共用核心:加權求和與取模只寫一份,由參數驅動。
這樣做的好處很直接:新增一支成員時只改資料,不動核心;出錯時也比對得出是哪一個參數寫錯。想先理解這條管線的上下游順序,可以讀號碼校驗的原理;想找一個用同一骨架處理長號碼的實例,讀IBAN 結構與校驗。
下一步
挑一條你手上真的會用到的編號,把它拆成「權重、模數、映射」三個欄位寫下來,再拿該制度公布的說明逐項核對。查完之後,可以用號碼校驗工具確認你的理解與工具算出來的結果是否一致,特別是那些餘數需要額外映射的案例。
本文比較的是公開演算法的參數差異;文中出現的示例數字純屬虛構,並非任何制度實際核發的號碼,也不代表任何帳戶、證件或商品確實存在。