blog/content/post/1970-01-01-00000053.md
2019-03-31 19:00:21 +08:00

281 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: クレジットカードの判別アルゴリズム
author: kazu634
date: 1969-12-31
url: /1970/01/01/_61/
wordtwit_post_info:
- 'O:8:"stdClass":13:{s:6:"manual";b:0;s:11:"tweet_times";i:1;s:5:"delay";i:0;s:7:"enabled";i:1;s:10:"separation";s:2:"60";s:7:"version";s:3:"3.7";s:14:"tweet_template";b:0;s:6:"status";i:2;s:6:"result";a:0:{}s:13:"tweet_counter";i:2;s:13:"tweet_log_ids";a:1:{i:0;i:4605;}s:9:"hash_tags";a:0:{}s:8:"accounts";a:1:{i:0;s:7:"kazu634";}}'
categories:
- gauche
- Lisp
---
<div class="section">
<p>
この前「<a href="http://d.hatena.ne.jp/kkomiyama/20090429/1240996163" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/kkomiyama/20090429/1240996163', 'あなたのクレジットカード番号に潜む規則性 &#8211; モスマン');" target="_blank">あなたのクレジットカード番号に潜む規則性 &#8211; モスマン</a>」を読んでいて、いつか実装しようと思っていたのだけれど、ついに完成。
</p>
<p>
基本的なアルゴリズムは
</p>
<blockquote title="あなたのクレジットカード番号に潜む規則性 - モスマン" cite="http://d.hatena.ne.jp/kkomiyama/20090429/1240996163">
<ol>
<li>
クレジットカード番号の後ろから数えて*1奇数桁だけを合計する。
</li>
<li>
クレジットカード番号の後ろから数えて偶数桁を2倍して合計する。2倍して10以上になるときは9を引く。
</li>
<li>
1と2の数字を合計する
</li>
</ol>
<p>
正しいカード番号ならこの合計が10で割り切れる。
</p>
<p>
<cite><a href="http://d.hatena.ne.jp/kkomiyama/20090429/1240996163" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/kkomiyama/20090429/1240996163', 'あなたのクレジットカード番号に潜む規則性 &#8211; モスマン');" target="_blank">あなたのクレジットカード番号に潜む規則性 &#8211; モスマン</a></cite>
</p>
</blockquote>
<p>
です。gaucheで順番に考えていきました。
</p>
<h4>
16桁かどうかの判別
</h4>
<p>
とりあえずカード番号は16桁だから、与えられた引数が16桁の数字かどうかを調べようと考えました。
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>chkdigit <span class="synStatement">number</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synConstant">16</span>digit? <span class="synStatement">number</span><span class="synSpecial">)</span>
<span class="synComment">;; 16桁だったら処理を開始</span>
#f <span class="synComment">;; 16桁じゃないから終了))</span>
</pre>
<p>
gaucheだと16桁かどうかというのは、正規表現を使えばいいと考えたのでrxmatchを使います。
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span><span class="synConstant">16</span>digit? <span class="synStatement">number</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span>rxmatch #/\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d/ <span class="synSpecial">(</span>number-&#62;string <span class="synStatement">number</span><span class="synSpecial">))</span>
#t
#f<span class="synSpecial">))</span>
</pre>
<h4>
奇数桁だけを合計する・偶数桁を2倍して合計するetc
</h4>
<p>
これらはおそらくfoldとmapを使えばできる。
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>fold <span class="synStatement">+</span> <span class="synConstant"></span> 奇数桁を集めたリスト<span class="synSpecial">)</span>
<span class="synSpecial">(</span>fold <span class="synStatement">+</span> <span class="synConstant"></span> <span class="synSpecial">(</span><span class="synStatement">map</span> <span class="synSpecial">(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span>n<span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">*</span> <span class="synConstant">2</span> n<span class="synSpecial">))</span> 偶数桁を集めたリスト<span class="synSpecial">))</span>
</pre>
<h4>
リストから奇数桁・偶数桁だけを取り出す
</h4>
<p>
与えられた16桁の数字をリストに変換しようと思ったので、この前Shiroさんに教えていただいた手続きをそのまま使います。
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>number-&#62;list n<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synStatement">loop</span> <span class="synSpecial">((</span>n n<span class="synSpecial">)</span> <span class="synSpecial">(</span>r <span class="synSpecial">'()))</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">&#60;</span> n <span class="synConstant">10</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">cons</span> n r<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">loop</span> <span class="synSpecial">(</span>quotient n <span class="synConstant">10</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">cons</span> <span class="synSpecial">(</span>modulo n <span class="synConstant">10</span><span class="synSpecial">)</span> r<span class="synSpecial">)))))</span>
</pre>
<p>
これで数字をリストに変換できたので、奇数桁と偶数桁だけを取り出す部分を考えます。
</p>
<p>
奇数桁というのは、リストの要素数が偶数の時の(car リスト)の結果になります。偶数桁というのは、リストの要素数が奇数の時の(car list)の結果になります。それを一つのリストにすればいいので:
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>even_list source<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">cond</span>
[<span class="synSpecial">(</span><span class="synStatement">null</span>? source<span class="synSpecial">)</span> <span class="synSpecial">'()</span>]
[<span class="synSpecial">(</span>even? <span class="synSpecial">(</span><span class="synStatement">length</span> source<span class="synSpecial">))</span> <span class="synSpecial">(</span>even_list <span class="synSpecial">(</span><span class="synStatement">cdr</span> source<span class="synSpecial">))</span>]
[<span class="synSpecial">(</span>odd? <span class="synSpecial">(</span><span class="synStatement">length</span> source<span class="synSpecial">))</span> <span class="synSpecial">(</span><span class="synStatement">cons</span> <span class="synSpecial">(</span><span class="synStatement">car</span> source<span class="synSpecial">)</span> <span class="synSpecial">(</span>even_list <span class="synSpecial">(</span><span class="synStatement">cdr</span> source<span class="synSpecial">)))</span>]<span class="synSpecial">))</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>odd_list source<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">cond</span>
[<span class="synSpecial">(</span><span class="synStatement">null</span>? source<span class="synSpecial">)</span> <span class="synSpecial">'()</span>]
[<span class="synSpecial">(</span>even? <span class="synSpecial">(</span><span class="synStatement">length</span> source<span class="synSpecial">))</span> <span class="synSpecial">(</span><span class="synStatement">cons</span> <span class="synSpecial">(</span><span class="synStatement">car</span> source<span class="synSpecial">)</span> <span class="synSpecial">(</span>odd_list <span class="synSpecial">(</span><span class="synStatement">cdr</span> source<span class="synSpecial">)))</span>]
[<span class="synSpecial">(</span>odd? <span class="synSpecial">(</span><span class="synStatement">length</span> source<span class="synSpecial">))</span> <span class="synSpecial">(</span>odd_list <span class="synSpecial">(</span><span class="synStatement">cdr</span> source<span class="synSpecial">))</span>]<span class="synSpecial">))</span>
</pre>
<p>
このような形になるかと思います。
</p>
<h4>
奇数桁だけを合計する・偶数桁を2倍して合計するetc
</h4>
<p>
奇数桁だけを合計するというのは、foldを使えばできそうです。
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>proc_odd <span class="synStatement">list</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>fold <span class="synStatement">+</span> <span class="synConstant"></span> <span class="synSpecial">(</span>odd_list リスト<span class="synSpecial">)))</span>
</pre>
<p>
「偶数桁を2倍して合計する。2倍して10以上になるときは9を引く」というのは、
</p>
<ol>
<li>
偶数桁のそれぞれを2倍する。2倍して10以上になるときは9を引く
</li>
<li>
その結果を足し合わせる
</li>
</ol>
<p>
足し合わせる部分はfoldを使えばできそうです:
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>proc_even <span class="synStatement">list</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>fold <span class="synStatement">+</span> <span class="synConstant"></span> 偶数桁のそれぞれを2倍する。2倍して10以上になるときは9を引いたリスト<span class="synSpecial">)</span>
</pre>
<p>
問題は「偶数桁のそれぞれを2倍する。2倍して10以上になるときは9を引く」の部分になります。この部分はmapを使えばいいはず。こんな感じで:
</p>
<pre class="syntax-highlight">
gosh&#62; <span class="synSpecial">(</span><span class="synStatement">map</span>
<span class="synSpecial">(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span>n<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">&#60;=</span> <span class="synConstant">10</span> <span class="synSpecial">(</span><span class="synStatement">*</span> <span class="synConstant">2</span> n<span class="synSpecial">))</span>
<span class="synSpecial">(</span><span class="synStatement">-</span> <span class="synSpecial">(</span><span class="synStatement">*</span> <span class="synConstant">2</span> n<span class="synSpecial">)</span> <span class="synConstant">9</span><span class="synSpecial">)</span> <span class="synComment">;; 10以上なら9を引く</span>
<span class="synSpecial">(</span><span class="synStatement">*</span> <span class="synConstant">2</span> n<span class="synSpecial">)))</span> <span class="synComment">;; そうでなければ、2倍する</span>
<span class="synSpecial">'(</span>1 2 3 4 5<span class="synSpecial">))</span> <span class="synComment">;; 処理対象のリスト</span>
<span class="synSpecial">(</span><span class="synConstant">2</span> <span class="synConstant">4</span> <span class="synConstant">6</span> <span class="synConstant">8</span> <span class="synConstant">1</span><span class="synSpecial">)</span>
</pre>
<p>
だから結局、
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>proc_even <span class="synStatement">list</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>fold <span class="synStatement">+</span> <span class="synConstant"></span> <span class="synSpecial">(</span><span class="synStatement">map</span>
<span class="synSpecial">(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span>n<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">&#60;=</span> <span class="synConstant">10</span> <span class="synSpecial">(</span><span class="synStatement">*</span> <span class="synConstant">2</span> n<span class="synSpecial">))</span>
<span class="synSpecial">(</span><span class="synStatement">-</span> <span class="synSpecial">(</span><span class="synStatement">*</span> <span class="synConstant">2</span> n<span class="synSpecial">)</span> <span class="synConstant">9</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">*</span> <span class="synConstant">2</span> n<span class="synSpecial">)))</span>
偶数桁のリスト<span class="synSpecial">)))</span>
</pre>
<p>
となります。
</p>
<h4>
正しいカード番号ならこの合計が10で割り切れる
</h4>
<p>
ここまででだいぶ部品ができてきたので、それを一つにまとめます:
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>judge_card リスト<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synSpecial">((</span>temp
<span class="synSpecial">(</span><span class="synStatement">+</span> <span class="synSpecial">(</span>proc_odd リスト<span class="synSpecial">)</span> <span class="synSpecial">(</span>proc_even リスト<span class="synSpecial">))))</span> <span class="synComment">;; ローカル変数tempに各桁の合計を代入</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">=</span> <span class="synSpecial">(</span>modulo temp <span class="synConstant">10</span><span class="synSpecial">)</span> <span class="synConstant"></span><span class="synSpecial">)</span> <span class="synComment">;; tempが10で割り切れる</span>
#t <span class="synComment">;; 割り切れたらカード番号は正しい</span>
#f<span class="synSpecial">)))</span> <span class="synComment">;; そうでなければカード番号は不正</span>
</pre>
<h4>
まとめ
</h4>
<p>
一番上位の手続きはchkdigitなので、それにこれまでの結果をまとめます。後、カード番号を逆順にするのも忘れずに:
</p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>chkdigit <span class="synStatement">number</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synSpecial">((</span>temp <span class="synSpecial">(</span><span class="synStatement">reverse</span> <span class="synSpecial">(</span>number-&#62;list <span class="synStatement">number</span><span class="synSpecial">))))</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synConstant">16</span>digit? <span class="synStatement">number</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>judge_card temp<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">print</span> <span class="synConstant">&#34;Card Number must be 16 digits.&#34;</span><span class="synSpecial">)</span>
<span class="synSpecial">)))</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>number-&#62;list n<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synStatement">loop</span> <span class="synSpecial">((</span>n n<span class="synSpecial">)</span> <span class="synSpecial">(</span>r <span class="synSpecial">'()))</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">&#60;</span> n <span class="synConstant">10</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">cons</span> n r<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">loop</span> <span class="synSpecial">(</span>quotient n <span class="synConstant">10</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">cons</span> <span class="synSpecial">(</span>modulo n <span class="synConstant">10</span><span class="synSpecial">)</span> r<span class="synSpecial">)))))</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span><span class="synConstant">16</span>digit? <span class="synStatement">number</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span>rxmatch #/\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d/ <span class="synSpecial">(</span>number-&#62;string <span class="synStatement">number</span><span class="synSpecial">))</span>
#t
#f<span class="synSpecial">))</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>even_list source<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">cond</span>
[<span class="synSpecial">(</span><span class="synStatement">null</span>? source<span class="synSpecial">)</span> <span class="synSpecial">'()</span>]
[<span class="synSpecial">(</span>even? <span class="synSpecial">(</span><span class="synStatement">length</span> source<span class="synSpecial">))</span> <span class="synSpecial">(</span>even_list <span class="synSpecial">(</span><span class="synStatement">cdr</span> source<span class="synSpecial">))</span>]
[<span class="synSpecial">(</span>odd? <span class="synSpecial">(</span><span class="synStatement">length</span> source<span class="synSpecial">))</span> <span class="synSpecial">(</span><span class="synStatement">cons</span> <span class="synSpecial">(</span><span class="synStatement">car</span> source<span class="synSpecial">)</span> <span class="synSpecial">(</span>even_list <span class="synSpecial">(</span><span class="synStatement">cdr</span> source<span class="synSpecial">)))</span>]<span class="synSpecial">))</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>odd_list source<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">cond</span>
[<span class="synSpecial">(</span><span class="synStatement">null</span>? source<span class="synSpecial">)</span> <span class="synSpecial">'()</span>]
[<span class="synSpecial">(</span>even? <span class="synSpecial">(</span><span class="synStatement">length</span> source<span class="synSpecial">))</span> <span class="synSpecial">(</span><span class="synStatement">cons</span> <span class="synSpecial">(</span><span class="synStatement">car</span> source<span class="synSpecial">)</span> <span class="synSpecial">(</span>odd_list <span class="synSpecial">(</span><span class="synStatement">cdr</span> source<span class="synSpecial">)))</span>]
[<span class="synSpecial">(</span>odd? <span class="synSpecial">(</span><span class="synStatement">length</span> source<span class="synSpecial">))</span> <span class="synSpecial">(</span>odd_list <span class="synSpecial">(</span><span class="synStatement">cdr</span> source<span class="synSpecial">))</span>]<span class="synSpecial">))</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>proc_odd <span class="synStatement">list</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>fold <span class="synStatement">+</span> <span class="synConstant"></span> <span class="synSpecial">(</span>odd_list <span class="synStatement">list</span><span class="synSpecial">)))</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>proc_even <span class="synStatement">list</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span>fold <span class="synStatement">+</span> <span class="synConstant"></span> <span class="synSpecial">(</span><span class="synStatement">map</span>
<span class="synSpecial">(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span>n<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">&#60;=</span> <span class="synConstant">10</span> <span class="synSpecial">(</span><span class="synStatement">*</span> <span class="synConstant">2</span> n<span class="synSpecial">))</span>
<span class="synSpecial">(</span><span class="synStatement">-</span> <span class="synSpecial">(</span><span class="synStatement">*</span> <span class="synConstant">2</span> n<span class="synSpecial">)</span> <span class="synConstant">9</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">*</span> <span class="synConstant">2</span> n<span class="synSpecial">)))</span>
<span class="synSpecial">(</span>even_list <span class="synStatement">list</span><span class="synSpecial">))))</span>
<span class="synSpecial">(</span>define <span class="synSpecial">(</span>judge_card <span class="synStatement">list</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synSpecial">((</span>temp
<span class="synSpecial">(</span><span class="synStatement">+</span> <span class="synSpecial">(</span>proc_odd <span class="synStatement">list</span><span class="synSpecial">)</span> <span class="synSpecial">(</span>proc_even <span class="synStatement">list</span><span class="synSpecial">))))</span>
<span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">=</span> <span class="synSpecial">(</span>modulo temp <span class="synConstant">10</span><span class="synSpecial">)</span> <span class="synConstant"></span><span class="synSpecial">)</span>
#t
#f<span class="synSpecial">)))</span>
</pre>
<h4>
「gauche」に関連する最近のエントリ
</h4>
<ul>
<li>
<a href="http://d.hatena.ne.jp/sirocco634/20090315/1237119679" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/sirocco634/20090315/1237119679', ' カレンダー関係の手続きを作ってみました &#8211; 武蔵の日記');" target="_blank"> カレンダー関係の手続きを作ってみました &#8211; 武蔵の日記</a>
</li>
<li>
<a href="http://d.hatena.ne.jp/sirocco634/20090207/1234017180" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/sirocco634/20090207/1234017180', ' 『プログラミングGauche』評価モデル &#8211; 武蔵の日記');" target="_blank"> 『プログラミングGauche』評価モデル &#8211; 武蔵の日記</a>
</li>
<li>
<a href="http://d.hatena.ne.jp/sirocco634/20090118/1232268322" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/sirocco634/20090118/1232268322', ' Emacs Lispのイディオム &#8211; 武蔵の日記');" target="_blank"> Emacs Lispのイディオム &#8211; 武蔵の日記</a>
</li>
<li>
<a href="http://d.hatena.ne.jp/sirocco634/20081124/1227537893" onclick="__gaTracker('send', 'event', 'outbound-article', 'http://d.hatena.ne.jp/sirocco634/20081124/1227537893', ' 仙台に帰省していました &#8211; 武蔵の日記');" target="_blank"> 仙台に帰省していました &#8211; 武蔵の日記</a>
</li>
</ul>
</div>