クレジットカードのチェックディジット
クレジットカード番号には規則性がある。
一見して区別はできないが,ある計算処理をした結果を見れば,それが真正な番号であるかそうでないかはすぐにわかる。
数字の各桁が特定の規則に従う場合,それぞれの桁について計算された数(チェックディジットと呼ばれる)を新たに数字の桁に付加することもあるし,計算された数字が特定の値になることを利用することもある。
クレジットカードの場合は後者である。クレジットカード番号の奇数桁と偶数桁について計算した最終結果の剰余が0になれば(つまり末尾が 0 になれば)正しいクレジットカード番号であるとする,ルーンアルゴリズムというのを採用している。
詳しい計算方法はプログラムを読めばわかることなので,ここでは述べない(ちゃんと知りたい人は,解説ページがいろいろあるので検索するとよい)。
以下が「クレジットカードのチェックディジット ルーンアルゴリズム」のプログラムである。
与えられた16桁の数字から計算される数値とそれが真正なものであるかどうか(true/false)を返す。
function isvalidcard(n)
magicnumber = 0
for (i, digit) in enumerate(Char[string(n)...])
digit = parse(Int, digit)
if i % 2 == 1
digit *= 2
digit > 9 && (digit -= 9)
end
magicnumber += digit
end
magicnumber, magicnumber % 10 == 0
end
isvalidcard (generic function with 1 method)
6922184350331903 は正しいカード番号であろうか?
isvalidcard(6922184350331903) # (63, false)
(63, false)
計算された数は 63 となり,末尾が 0 でないので,真正なカード番号ではないことがわかる。
3566002020360505 は正しいカード番号であろうか?
isvalidcard(3566002020360505) # (50, true)
(50, true)
計算された数は 50 となり,末尾が 0 なので,真正なカード番号であることがわかる。
では,出鱈目に作成された 16 桁の数字が真正なカード番号である確率はどのくらいであろうか?
計算しないで,瞬時に正解が思い浮かんだ人は鋭い!
例えば 15 桁の数,123456789012345 の末尾に,例えば 0 を付して,1234567890123450 という番号を作って,それが真正なカード番号かどうか調べてみる。(最も,そのうちの何桁かはカード発行会社の識別番号だったりするので,必ずしもそれが実在しうるかどうかは別であるが)
isvalidcard(1234567890123450) # (58, false)
(58, false)
計算結果が 58 で末尾が 0 になるには 2 足りない。
つまり,1234567890123452 なら真正なカード番号だ。
isvalidcard(1234567890123452) # (60, true)
(60, true)
最初の 15 桁の数が 123456789012345 の場合,16 桁目の数字は 0〜9 通りあるが,真正な番号になるのはそのうちのひとつだけである
他のどの 1 桁についても,同じことがいえる。
つまり,ランダムに生成された 16 桁の数字がクレジットカード番号として真正なものである確率は 1/10 であるということだ。
人によっては,これは由々しき問題だと思うかもしれないが,実際には(最近というかかなり前から)セキュリティーコードと呼ばれる3,4桁の付加数字が要求されるようになっているし,名義人の名前も必要なのであまり心配する必要はないだろう。
1000000000000000〜9999999999999999 のランダムな番号を生成し,それが申請なクレジット番号である割合を求めるプログラムは以下の通り。
n = 10^6
validcard = 0
for i in rand(1000000000000000:9999999999999999, n)
isvalidcard(i)[2] && (validcard += 1)
end
println("valid card rate = $(validcard/n)")
valid card rate = 0.10012
理論どおり,ほぼ 1/10 となっていることが確認できる。