Luhn Algorithm- Returning True for a False Credit

2019-08-29 07:49发布

问题:

Having a little issue with my code to see if a credit card number adheres to the Luhn Algorithm. The code is returning true when the Credit Card is divisible by 10, but also is returning true when the CC number is not divisible by 10. I have printed out the final sum to make sure the numbers were actually adding to the sum variable, and they seem to be.. Below is my code. I know it can be cleaner, but at this stage I would like to see it work first.

def check_card

   c_num= []

   sum=0

   s_numbers=@card_numbers.to_s.reverse.split("")

   s_numbers.each_slice(2) do |x| 
   c_num << (x.last.to_s.to_i*2)
   c_num << (x.first.to_s.to_i)
     end

  c_num.each do |num|
    if num.to_i > 9
      sum+= (num.to_i % 10)+1
    else 
      sum += num.to_i
    end
  end

sum % 10==0 

end

Here is how it is being called:

it 'returns false for a bad card' do
  card = CreditCard.new(4408041234567892)
  card.check_card.should eq false
end

回答1:

This is your code, commented and adapted to behave like the wikipedia description.

def check_card(str) #creditcardnumber as argument

   c_num = []

   sum = 0

   s_numbers = str.split("") #no reversing. str.split("").map(&:to_i) would save a lot of to_i's later on...
   checksum = s_numbers.pop.to_i #chop off last digit, store as checksum

   s_numbers.each_slice(2) do |x| 
     c_num << (x.last.to_s.to_i*2)
     c_num << (x.first.to_s.to_i)
   end

  c_num.each do |num|
    if num.to_i > 9
      sum+= (num.to_i % 10)+1
    else 
      sum += num.to_i
    end
  end

  (sum * 9) % 10 == checksum 

end

p check_card("79927398713") #=> true


回答2:

Now that another answer has appeared I will offer a suggested coding. This does not answer your question, but I thought it might be of interest and couldn't very well put it in a comment, because of formatting limitations.

def valid?(card)
  return false unless card  =~ /^\d+$/ # Ensure card contains only digits
  arr = card.split('').reverse.each_with_index.map {|d, index| (index.odd? ? 2*(d.to_i) : d.to_i)}
  (arr.join.split('').inject(0) {|tot, d| tot + d.to_i}) % 10 == 0
end 

valid?("1234567890123456") => false


回答3:

It seems you're confusing the output of the test with the output of the check_card method. You've confirmed that sum == 69, so sum % 10 == 0 should return false (unless Ruby's math is broken), so your check_card method should also return false — add the line puts card.check_card after the line card = CreditCard.new(4408041234567892) (but before the next line) in your testing block to show the value returned.

The next line, card.check_card.should eq false, asserts that the returned value "should equal" false — that is, it expects check_card to be false for that value of @card_numbers, and will return true if that's the case. I suspect that you're seeing the test come up true and taking that to mean the method is returning the incorrect value of true.

Without seeing the output of the test, this is only speculation, of course. However, your code seems correct and, for me, gives the correct result.



回答4:

posting my answer because I thought it was clean:

def valid?(card_number)
  return false unless card_number !~ /\D/

  card_numbers = card_number.reverse.split("").map(&:to_i)
  sum = 0
  card_numbers.each_with_index do |num, i|
    if i.even?
      sum += num
    else
      sum += (num *= 2) > 9 ? num.divmod(10).inject(:+) : num
    end
  end
  sum % 10 == 0
end


标签: ruby luhn