【问题标题】:Luhn Algorithm- Returning True for a False Credit CardLuhn 算法 - 为假信用卡返回真值
【发布时间】:2013-10-01 11:04:10
【问题描述】:

我的代码有点问题,看看信用卡号是否符合 Luhn 算法。当信用卡可被 10 整除时,代码返回 true,但当 CC 编号不能被 10 整除时,代码也返回 true。我已打印出最终总和以确保这些数字实际添加到 sum 变量中,并且他们似乎是.. 下面是我的代码。我知道它可以更清洁,但在这个阶段我希望先看到它工作。

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

以下是它的调用方式:

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

【问题讨论】:

  • 你的代码对我有用。您使用的输入是什么?
  • 特别有趣,你的代码也给了我这个数字的错误。
  • 您确定这正是您使用的代码吗?
  • 阳性。我在它上面有一个初始化方法(用于类),但它所做的只是定义局部变量并在信用卡号不够长时发送 ArguementError。我发给你的号码总和一直是 69,所以应该是假的..

标签: ruby luhn


【解决方案1】:

现在已经出现了另一个答案,我将提供一个建议的编码。这不能回答您的问题,但我认为它可能很有趣,并且由于格式限制,无法很好地将其放在评论中。

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

【讨论】:

  • +1 好的 Ruby 式答案。我可能会建议,由于您正在验证信用卡号,请检查它是否有 16 位数字(如果以 3 开头,则为 15 位,对于 AmEx)。
  • 是的,我想我只是假设四个主要的使用 Luhn(并且没有必要检查任何使用不同长度的)。
  • @iamnotmaynard 您的建议已被采纳。它促使我做最少的调查。这个Wiki 告诉我的信息比我想知道的更多。事实证明,它们的范围从 12 到 19 位,大多数(但不是全部)使用 Luhn。 (此评论用链接错误的评论替换了之前的评论。)
【解决方案2】:

这是您的代码,经过注释和调整,其行为类似于 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

【讨论】:

  • 感谢您的帮助,但代码现在显示参数 (4408041234567893) 为假 - 应该为真。另外,我在这个问题上看到了很多 .map(&:to_i) 。每当我看到它都会调用 Proc 或 Lambda。我们究竟在用这里的代码做什么。
  • 试试check_card("4408041234567893") - 它需要一个字符串。不用担心 Lambda 和 Procs,map.to_i 会将 to_i 方法应用于由 split("") 产生的数组的每个元素:只是可以操作的好整数..
  • @user2201289 你问的是“4408041234567893”还是“440804l234567893”?那个抄送号码很熟悉。
【解决方案3】:

您似乎将测试的输出与check_card 方法的输出混淆了。你已经确认sum == 69,所以sum % 10 == 0应该返回false(除非Ruby的数学被破坏了),所以你的check_card方法也应该返回false——在@987654328行之后添加puts card.check_card行@(但在下一行之前)在您的测试块中显示返回的值。

下一行,card.check_card.should eq false,断言返回的值“应该等于”false——也就是说,对于@card_numbers的值,它期望check_cardfalse,并将返回@987654334 @如果是这样的话。我怀疑您看到测试出现 true 并认为该方法返回的 true 值不正确。

没有看到测试的输出,这当然只是猜测。但是,您的代码似乎是正确的,并且对我而言,给出了正确的结果。

【讨论】:

    【解决方案4】:

    发布我的答案是因为我认为它很干净:

    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
    

    【讨论】:

      猜你喜欢
      • 2021-07-21
      • 2019-03-11
      • 2013-10-21
      • 2012-01-05
      • 2017-01-09
      • 2012-02-29
      • 1970-01-01
      • 2014-12-25
      • 2014-09-07
      相关资源
      最近更新 更多