當表單在你還沒按下確認鍵之前就說「末位不對」,背後幾乎都是 Luhn 演算法在做判斷。它由當時任職於 IBM 的 Hans Peter Luhn 在 1950 年代提出,後來透過 ISO/IEC 7812 成為卡號領域的標準校驗方式。名字聽起來像密碼學,實際上只是一道加權相加、再看能不能整除 10 的算術題。搞懂它,你就能替任何合法前綴補出正確的末位,也能在別人回報「卡號被拒絕」時,快速分辨那到底是格式問題還是別的原因。
為什麼卡號要留一位校驗位?
卡號是人工輸入的。早年沒有掃卡、沒有感應,收銀員一個數字一個數字打進終端機,打錯的代價是一通打向發卡行的失敗授權,以及櫃檯前多等三十秒的客人。如果能在送出請求之前就發現「這串數字哪裡怪怪的」,整個流程會順暢許多。
於是卡號的最後一位被拿來當作檢查用:前面的位數可以任意決定,末位則被選成讓整串數字的加權總和剛好是 10 的倍數。這一招不需要資料庫、不需要連線,只要會加法就能當場判斷——對 1950 年代的設備來說,這是非常務實的設計。
Luhn 的計算步驟
規則本身只有三句話,但每一句都有容易誤解的地方。
以一個 16 位號碼為例,從最右邊數來第二位開始,每隔一位把數字加倍;其餘位保持原值。加倍之後如果結果大於 9,就把它減 9(等價於把兩個位數相加,例如 8 加倍得 16,1 加 6 等於 7)。最後把所有位數相加,總和能被 10 整除就通過,否則不通過。
三個關鍵細節:
- 加倍的方向是從右邊數起,不是從左邊。號碼的總位數會決定哪些位被加倍,所以 15 位與 16 位被加倍的會是完全不同的兩組位置。
- 減 9 這個動作只在加倍後大於 9 時需要,但實作時一定要處理,否則遇到 8 或 9 就會算錯。
- 處理的單位是「位」而不是「數」,這在撰寫程式時常被誤解,也是很多實作在遇到前導零時出錯的原因。
用 4242 這組號碼手算一次
拿一組由本站工具產生、且被廣泛引用的通用測試卡號來示範:16 位、由 4 與 2 交替排列。這串數字結構正確,卻從未發行給任何人,正好適合拿來對照,因為所有位置的值都一樣。
| 檢視方式 | 內容 | 小計 |
|---|---|---|
| 被加倍的那 8 位 | 8 8 8 8 8 8 8 8 | 64 |
| 保持原值的那 8 位 | 2 2 2 2 2 2 2 2 | 16 |
| 總和 | — | 80 |
先看 4 的那些位:每一個 4 加倍後是 8,8 仍然小於 10,所以不加也不減,八個位置合計 64。再看 2 的那些位:它們不需要加倍,八個位置合計 16。兩者相加是 80,能被 10 整除,這串號碼因此通過校驗。
這個例子刻意選得簡單,因為它不會觸發「加倍後大於 9」的規則。實際拿一串隨機號碼來算時,你幾乎一定會遇到 6 或 8 這類加倍後超過 9 的位數,那才是手算最容易漏掉的一步。
Luhn 還被用在哪些地方
同一道算式出現在很多意想不到的位置:國際銀行帳號的外層檢查、加拿大社會保險號碼、部分國家的身分證字號欄位、若干電信與公用事業的客戶編號,以及許多內部系統自己設計的序號。它的優點是實作極短、不需要查表,因此常被拿來當作「第一層防護」。
廣泛使用也帶來副作用:它是程式面試與練習題的常客,網路上因此流通著大量品質落差很大的實作。挑選時記得回到規格本身,別只看它跑不跑得動。
Luhn 演算法能抓出哪些錯誤?
它能抓出所有「單一位置打錯」的情況,也能抓出絕大多數相鄰兩位互換的錯誤,但不是全部。經典的漏網之魚是 0 與 9 這類互換:0 加倍後仍然是 0,兩者算出來的總和相同,錯誤就被放過了。相隔更遠的換位、整段數字被貼錯,也同樣偵測不到。
還有一件必須說清楚的事:它和安全性毫無關係。任何知道前綴的人都能在半秒內造出無限多組通過校驗的號碼,本站的卡號產生器就是靠這個原理運作,而且是公開的。它既不能證明帳戶存在,也不能證明對方手上有實體卡片,只能當成錯字偵測器。
給開發者:實作時的順序與常見錯誤
真正容易出事的不是算式,而是它周圍的前置處理。幾個反覆出現的坑:
- 先清掉分隔符號。 使用者會貼進帶空格或連字號的格式,驗證之前必須只留數字。
- 不要用整數型別處理整串號碼。 19 位數會超過許多語言 32 位整數的上限,而且前導零會被吃掉,位數判定就跟著錯。
- 從右邊開始數。 方向搞反在奇數長度的號碼上會得到完全不同的結果,這是實作最常見的錯誤來源。
- 把結果的語意分開。 長度不對、含有非數字字元、校驗失敗是三種不同的狀況,混成同一個布林值會讓呼叫端無法給出正確提示。
驗證的順序建議是先檢查長度、再檢查字元、最後才跑校驗和,這樣錯誤訊息才具體。這套順序的完整討論見驗證信用卡號。測試案例至少要涵蓋:正確號碼、只改動一位的號碼、相鄰兩位對調的號碼、長度不足、以及含有非數字字元的輸入。
下一步
想從格式角度理解卡號的前綴與長度怎麼組成,讀信用卡號格式;需要各卡組織的號段與長度差異,讀各卡組織測試卡號。只想先拿幾組能通過驗證的號碼來試,直接開虛擬信用卡工具產生就好。