【问题标题】:How is a check digit calculation done in Ruby?在 Ruby 中如何进行校验位计算?
【发布时间】:2011-08-29 12:47:22
【问题描述】:

我正在尝试在 Ruby 中为 FedEx 跟踪号码构建校验位计算。

这里是校验位计算的信息和步骤:

  • 数字位置从右到左标记。
  • 数字 1 是校验字符。
  • 未使用数字 16 到 22。

步骤:

  1. 从位置 2 开始,将 偶数 个位置的值相加。
  2. 将步骤的结果乘以 3。
  3. 从位置 3 开始,将奇数位置的值相加。请记住 - 位置 1 是您要计算的校验位。
  4. 将第二步的结果与第三步的结果相加。
  5. 确定最小的数字,当它与第四步中的数字相加时得到 10 的倍数。这是校验位。

以下是流程示例(由 FedEx 提供):

那么,我该如何在 Ruby 中实现呢?

【问题讨论】:

  • 描述似乎相当清楚。有没有什么特别让你很难受的事情?
  • 我只是不知道从哪里开始实施这些步骤的逻辑。

标签: ruby algorithm check-digit


【解决方案1】:

当您将数字作为字符串时(或者如果您将数字作为整数,只需 #to_s 并获取字符串),然后您可以简单地从那里提取数字:

number_string[idx].to_i

或者如果你使用 Ruby 1.8

number_string[idx..idx].to_i

#to_i是把它转成整数,这样你就可以把它加给别人了。然后继续执行提供的步骤来计算您的数字。

实现它所需要做的就是将指令中提供的位置正确映射到数字字符串表示中的idx 索引位置。只需在纸上计算 head 或在 Ruby 中使用负 idx(从字符串末尾开始计算)。

编辑

解决方案可能是这样的:

bar_code_data = "961102098765431234567C"
digits_with_position = bar_code_data.reverse[1..14].split(//).map(&:to_i).zip(2..1/0.0)

如下:

  • reverse - 反向字符串,所以现在我们可以从左到右计数而不是反向
  • [1..14] - 选择我们感兴趣的字符子串(Ruby 从 0 开始计数)
  • split(//) - 将一个字符串拆分为长度为 1 个字符的子字符串,换句话说 - 单独的数字
  • map(&:to_i) - 对数组的每个元素调用 #to_i,换句话说,转换为整数
  • zip(2..1/0.0) - 为每个元素添加从 2 到 Infinity 的位置

现在我们应该有这样的东西:

[[7, 2], [6, 3], [5, 4], [4, 5], [3, 6], [2, 7], [1, 8], [3, 9], [4, 10], [5, 11], [6, 12], [7, 13], [8, 14], [9, 15]]

sum = digits_with_position.map{|i| i[0] * (i[1].even? ? 3 : 1)}.reduce(+:)

我们在算法上做了一些小改动,你应该不难理解:

代替:

sum = (in[2] + in[4] + in[6] + ...)*3 + (in[3] + in[5] + in[7] + ...)

我们做了:

sum = in[2]*3 + in[3]*1 + in[4]*3 + in[5]*1 + in[6]*3 + in[7]*1 + ...

这是相同的结果,但操作顺序有所不同。

还有:

  • map {|i| ... } - 映射列表的每个值,在我们的例子中 i 是元组,一对 [digit,pos]
  • i[1].even? - 检查位置是否偶数
  • i[1].even? ? 3 : 1 - 偶数位置使用 3,相反(奇数)位置仅使用 1
  • reduce(:+) - 使用 + 操作将结果数组减少为单个值(添加所有结果)

现在有趣的部分:-)

check_code = 10 - (sum % 10)
  • sum % 10 - 和值的模块 10,返回除以 10 的提醒,在我们的例子中是最后一位
  • 10 - (sum % 10) - 补到最接近的不小于 10 的倍数

描述有误,因为如果你的结果是130,那么10的下一个更大的倍数是140,差是10,这不是数字的正确结果(应该是0)。

其他更快的解决方案是这样的(展开所有循环,只需对所有内容进行硬编码):

d = "961102098765431234567C".split(//) # avoid having to use [-2..-2] in Ruby 1.8
sum_even = d[-2].to_i + d[-4].to_i + d[-6].to_i + d[-8].to_i + d[-10].to_i + d[-12].to_i + d[-14].to_i
sum_odd = d[-3].to_i + d[-5].to_i + d[-7].to_i + d[-9].to_i + d[-11].to_i + d[-13].to_i + d[-15].to_i
sum = sum_even * 3 + sum_odd
check_code = 10 - sum % 10

这只是简单的解决方案,不值得解释,除非有人要求

【讨论】:

  • 将它与even?odd?each_with_index 结合起来,它应该是小菜一碟。
  • 它 OP 在 Ruby 中不流利,那么它可能是事件硬编码的索引集,没关系。跟踪号码是固定长度的字符串。
  • @Benoit:我不一定会“流利”Ruby,但我很想看看你会如何使用even?odd?each_with_index
  • @Shpigford index.odd?index.even?number_string.chars.each_with_index {|char, index| ... your code goes here ... }。这就是你的想法吗?
【解决方案2】:

将您的号码传递给以下方法,它将返回附加校验和数字的号码。 参考来自:https://www.gs1.org/services/how-calculate-check-digit-manually

def add_check_digit(code_value) 
   sum = 0
   code_value.to_s.split(//).each_with_index{|i,index| sum = sum + (i[0].to_i * ((index+1).even? ? 3 : 1))}
   check_digit = sum.zero? ? 0 : (10-(sum % 10))
   return (code_value.to_s.split(//)<<check_digit).join("")
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-08
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 2013-03-13
    相关资源
    最近更新 更多