Реализация алгоритма Луна в Ruby - PullRequest
5 голосов
/ 08 февраля 2012

Я пытался реализовать алгоритм Луна в Ruby. Я следовал за этими шагами:

  • Формула проверяет число по включенной контрольной цифре, которая обычно добавляется к частичному номеру счета, чтобы получить полный номер счета. Этот номер счета должен пройти следующий тест:
    • Считая от контрольной цифры, которая находится справа, и двигаясь влево, удваивает значение каждой второй цифры.
    • Суммируйте цифры продуктов (например, 10 = 1 + 0 = 1, 14 = 1 + 4 = 5) вместе с недвоенными цифрами от исходного числа.
    • Если сумма по модулю 10 равна 0 (если сумма заканчивается на нуле), то число действует в соответствии с формулой Луна; иначе это недействительно.

http://en.wikipedia.org/wiki/Luhn_algorithm

Вот что я придумал:

 def validCreditCard(cardNumber)
    sum = 0
    nums = cardNumber.to_s.split("")
    nums.insert(nums.size, "x")
    nums.reverse!
    nums.each_with_index do |n, i|
        if !n.eql?("x")
            sum += (i % 2 == 0) ? n.to_i : n.to_i * 2
        end
    end
    if (sum % 10) == 0
        return true
    else
        return false
    end
end

Однако, это возвращает false каждый раз, когда я проверяю это. Я не уверен, что я делаю неправильно.

Ответы [ 10 ]

20 голосов
/ 08 февраля 2012

Вот быстрый, который работает:

def credit_card_valid?(account_number)
  digits = account_number.chars.map(&:to_i)
  check = digits.pop

  sum = digits.reverse.each_slice(2).flat_map do |x, y|
    [(x * 2).divmod(10), y || 0]
  end.flatten.inject(:+)

  check.zero? ? sum % 10 == 0 : (10 - sum % 10) == check
end

credit_card_valid? "79927398713" #=> true
credit_card_valid? "79927398714" #=> false
10 голосов
/ 05 июня 2013

У меня были проблемы с запуском большого количества приведенного выше кода, поэтому я пошел дальше и разработал решение. Ниже представлено мое решение, а также тестовый код, который можно запустить с Ruby-ядрами библиотеки AWESOME minitest.

require "minitest"
require "minitest/autorun"

# Public: Validates number against Luhn 10 scheme
#
# Luhn Algo ripped from: http://en.wikipedia.org/wiki/Luhn_algorithm
# 1. From the rightmost digit, which is the check digit, moving left, double the value of every second digit; if product of this doubling operation is greater than 9 (e.g., 7 * 2 = 14).
# 2. Sum the digits of the products (e.g., 10: 1 + 0 = 1, 14: 1 + 4 = 5) together with the undoubled digits from the original number.
# 3. If the total modulo 10 is equal to 0 (if the total ends in zero) then the number is valid according to the Luhn formula; else it is not valid.
#
# Returns true or false
def luhn_valid?(cc_number)
  number = cc_number.
    gsub(/\D/, ''). # remove non-digits
    reverse  # read from right to left

  sum, i = 0, 0

  number.each_char do |ch|
    n = ch.to_i

    # Step 1
    n *= 2 if i.odd?

    # Step 2
    n = 1 + (n - 10) if n >= 10

    sum += n
    i   += 1
  end

  # Step 3
  (sum % 10).zero?
end

describe "Luhn Algorithm" do
  # Cards ripped from paypal: http://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm
  CC_NUMBERS = {
    "American Express" => "378282246310005",
    "American Express 2" => "371449635398431",
    "American Express Corporate" => "378734493671000",
    "Australian BankCard" => "5610591081018250",
    "Diners Club" => "30569309025904",
    "Diners Club 2" => "38520000023237",
    "Discover" => "6011111111111117",
    "Discover 2" => "6011000990139424",
    "JCB" => "3530111333300000",
    "JCB 2" => "3566002020360505",
    "MasterCard" => "5555555555554444",
    "MasterCard 2" => "5105105105105100",
    "Visa" => "4111111111111111",
    "Visa 2" => "4012888888881881",
    "Visa 3" => "4222222222222",
  }

  it "returns true for valid numbers" do
    assert CC_NUMBERS.values.all? { |number| luhn_valid?(number) }
  end

  it "returns false for invalid numbers" do
    CC_NUMBERS.values.each do |number|
      me_turn_bad = (number.to_i + 1).to_s
      refute luhn_valid?(me_turn_bad)
    end
  end
end
1 голос
/ 11 июля 2015

Некоторые классные ответы выше.Я хотел написать свое собственное решение;для потомков я добавляю его сюда.

def number_is_luhn_valid?(credit_card_number)
  cc_digits = credit_card_number.to_s.reverse.chars.map(&:to_i)
  check_sum = 0

  cc_digits.each_slice(2) do |odd, even|
    check_sum += odd
    next unless even
    even *= 2
    even = even.divmod(10).inject(:+) if even > 9
    check_sum += even
  end

  return check_sum.modulo(10) == 0
end

Всегда рад услышать отзывы, если они у вас есть!

0 голосов
/ 15 февраля 2019

Мы используем алгоритм Луна для проверки действительности банковских карт.

Длина номера банковской карты (PAN) в соответствии с ISO / IEC 7812-1: 2017 составляет от 10 до 19.

Поэтому мое решение:

def card_valid?(card_number)
  return false unless card_number.match /\A\d{10,19}\z/
  sum = 0
  card_number.reverse.split('').map(&:to_i).each.with_index(1) do |d, i|
    d *= 2 if i.even?
    d -= 9 if d > 9
    sum += d
  end
  (sum % 10).zero?
end

card_valid? '1115' #=> false
card_valid? '4417123456789112' #=> false
card_valid? '4408041234567893' #=> true
0 голосов
/ 08 марта 2017
    #!/usr/bin/env ruby
number = ARGV[0]
## making array from input number having each digit at separate index
digits = number.chars.map(&:to_i)
check = digits.pop

sum = digits.reverse.each_slice(2).flat_map do |x, y|
  [(x * 2).divmod(10), y || 0]
end.flatten.inject(:+)
## validating if sum is nil then assiging it zero, when we pass single digit like 4 or 5 or 9 then sum will be nil
sum = sum ? sum : 0
puts (check.zero? ? sum % 10 == 0 : (10 - sum % 10) == check) ? "#{number} is valid" : "#{number} is invalid"

Решение, данное Майклом, нарушается, если вы введете однозначное число в качестве ввода, этот код будет обрабатывать все случаи.

0 голосов
/ 01 ноября 2012

Я наткнулся на этот драгоценный камень Ruby для алгоритма Луна: https://github.com/joeljunstrom/ruby_luhn

0 голосов
/ 09 февраля 2012

Я благодарю Майкла за помощь в реализации моего решения.

Это может быть не так эффективно, но это работает, и я понимаю Луна и реализацию с моей стороны.

Я все еще учусь, но вот оно:

 def validCreditCard?(cardNumber)
    sum = 0 
    digits = []
    nums = cardNumber.to_s.split("")
    nums.reverse.each_slice(2) do |n|
        digits << (n.last.to_i * 2)
        digits << n.first.to_i
    end
    digits.each do |n|
        stringNum = n.to_s
        if stringNum.length == 2
            tempNums = stringNum.split("")
            sum += tempNums.first.to_i + tempNums.last.to_i
        else
            sum += stringNum.to_i
        end
    end
    sum%10 == 0
end
0 голосов
/ 08 февраля 2012

Я немного изменил вашу реализацию. Теперь это работает

def validCreditCard(cardNumber)
  sum = 0
  nums = cardNumber.to_s.split("")
  nums.each_with_index do |n, i|
    sum += if (i % 2 == 0)
             n.to_i * 2 >9 ? n.to_i*2-9 : n.to_i*2
           else
             n.to_i
           end
  end
  if (sum % 10) == 0
    return true
  else
    return false
  end
end
0 голосов
/ 08 февраля 2012

Вот ваше решение:

def validate(cardNumber)
  nums = cardNumber.to_s.split("")
  checkdigit = nums[nums.length - 1]
  nums[nums.length - 1] = 0
  nums.reverse!
  sum=0
  for i in 1..nums.length
    if i%2==0 
      sum = sum + nums[i].to_i  
      next
    end
    nums[i] = (nums[i].to_i*2 < 10 ) ? (nums[i].to_i*2) : (nums[i].to_i*2 - 9)
    sum = sum + nums[i].to_i
  end
  puts (10 - sum%10).to_i == checkdigit.to_i
end

validate(79927398713)

Это не оптимизировано, но вы можете сделать эти незначительные изменения самостоятельно:)

0 голосов
/ 08 февраля 2012

Я думаю, проблема в том, что вы не учли сдвиг индекса, который вы получили, вставив "x" в конце массива, поэтому вы удваиваете неправильные числа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...