LoginSignup
8
11

More than 5 years have passed since last update.

クレジットカードのチェックディジットをrubyで作る

Last updated at Posted at 2017-11-23

初めに

クレジットカードはルーンアルゴリズムというもので出来ているそうです。

今回はrubyでこのアルゴリズムを作ってみたので、みなさんに見ていただこうと思いました。

イケてない書き方や、無駄な書き方があればおしえていただけると大変勉強になります。

ルーンアルゴリズムとは

クレジットカードの奇数桁の総和と、偶数桁値を二倍した値の総和を足すと必ず10で割り切れるというものです。

精密には、偶数桁が2倍して2桁(5,6,7,8,9)になるものは、1桁目と2桁目の数字を足したものを偶数桁の総和へ足します。

この仕組を利用することで、クレジットカード番号の打ち間違えを防いでいるそうです。


#正しいクレジットカードの番号
8460877291285697

8 6 8 7 9 2 5 9 |7+3+7+5+9+4+1+9|=17+19+9=28+17=45

 4 0 7 2 1 8 6 7|13+15+7|=35

(35 + 45) % 10 == 0
#Trueを返す

プログラム

16番目の数字は1-15番目のランダムな数字から決まるので、846087729128569Yとなります。

今回Y(16番目)に入る数字を探すプログラムを書きました。

check_digit.rb

# 何個のクレジットカードを作るか、この入力値で決める。
# ループの回数を標準入力から取得。変数はintegerに変える
count = gets.chomp.to_i
odd = 0
even = 0
count.times do
  # digitにはまだstringの数字が入っている
  digit = gets.chomp.split('')
  # クレジットカードは16桁
  16.times do |i|

    correct_count = i+1

    if correct_count.even?
      # iが15番目(16桁目)で、値がXの場合は処理を行わない
      # 数字にして、足す
      if correct_count == 16
        10.times do |r|
           if (odd+even+r) % 10 == 0
             puts "答えは:#{r}"
             break
           end
        end
        break
      end
      digit[i] = digit[i].to_i
      even += digit[i]
  elsif correct_count.odd?
      # 最初にintにして、数を二倍にする。
      digit[i] = digit[i].to_i * 2
      # 文字列に変換して、桁が2桁になる場合は、10の位と1の位を足す
      if digit[i].to_s.length == 2
        # 文字列にする
        digit[i] = digit[i].to_s
        digit[i] = digit[i].split('')
        # 1の位と10のくらいを足す計算
          a = digit[i][0].to_i
          b = digit[i][1].to_i
        # evenに足す
        even = even + a + b
      else
        # 二倍にしても、1桁の場合
        # integerにして、evenに足す
        digit[i] = digit[i].to_i
        even += digit[i]
      end
    end
    # 0-14番目(1-15番目)までの数字は計算し終わっているので
  end
  odd = 0
  even = 0
end

ターミナル.rb
#ループ回数
1
#16桁の数字と文字
846087729128569Y

答えは:7

P.S. @scivolaさんにコメントいただいた方法でコードを編集してみました!

count = gets.to_i
odd = 0
even = 0
count.times do
  digits = gets.chars.map(&:to_i)
  16.times do |i|
    correct_count = i+1
    if correct_count.even?
      if correct_count == 16
        10.times do |r|
           if (odd+even+r) % 10 == 0
             puts r
             break
           end
        end
      end
      even += digits[i]
    elsif correct_count.odd?
      odd_number = digits[i]* 2
      if 10 <= odd_number
        odd_number = odd_number.divmod(10).sum
        odd += odd_number
      else
        odd += odd_number
      end
    end
  end
  # ループごとに初期化
  odd = 0
  even = 0
end

mapの引数がわからなかったので、この記事を参考にしました!
@kenzan100さん【Rubyの array.map(&:to_s) 記法を紐解く
リスト内の要素を順に呼んで、型を変えてくれるんですね!

52行から31行に減りました!毎度行っていたintegerへの変更ともおさらばです!!

またコードが短くなった!!

count = gets.to_i
count.times do
  odd = 0
  even = 0
  digits = gets.chars.map(&:to_i)
  digits.each.with_index(1) do |digit, correct_count|
    if correct_count.even?
      if correct_count == 16
        10.times do |r|
           if (odd+even+r) % 10 == 0
             puts r
             break
           end
        end
      end
      even += digits[digit]
    else
      odd_number = digits[digit]* 2
        odd_number = odd_number.divmod(10).sum if 10 <= odd_number
        odd += odd_number unless 10 <= odd_number
    end
  end
end

32行→23行になりました!こんなに綺麗にかけるとは思っていませんでした😅

digits.each.with_index(1) do |digit, correct_count|

調べてみたのですが、この行はmapでもかけるそうです!

digits.map.with_index(1) do |digit, correct_count|

with_index便利すぎます!

参考@awakiaさん:【with_indexが便利だという話とstable_sort_by

最後に

やっぱりプログラミングは面白いです!!

次はpythonでもっと短く書いてみようと思います!

最後まで見てくださり、ありがとうございました(^^)

8
11
11

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
11