Как вычисляется контрольная цифра в Ruby?
Я пытаюсь построить расчет контрольных цифр в Ruby для номеров отслеживания FedEx.
Вот информация и шаги для вычисления контрольной цифры:
- Цифровые позиции обозначены справа налево.
- Цифра 1 - символ проверки.
- Цифры с 16 по 22 не используются.
шаги:
- Начиная с позиции 2, сложите значения четных позиций.
- Умножьте результаты шага один на три.
- Начиная с позиции 3, сложите значения нечетных позиций. Помните - позиция 1 - это контрольная цифра, которую вы пытаетесь вычислить.
- Добавьте результат второго шага к результату третьего шага.
- Определите наименьшее число, которое при добавлении к номеру из шага четыре дает кратное 10. Это контрольная цифра.
Вот пример процесса (предоставлен FedEx):
Итак, как мне реализовать это в Ruby?
2 ответа
Когда у вас есть номер в виде строки (или если у вас есть цифра в виде целого числа, просто #to_s
на нем и получите строку), а затем вы можете просто извлечь цифры оттуда с помощью:
number_string[idx].to_i
или если вы используете Ruby 1.8
number_string[idx..idx].to_i
#to_i
это преобразовать его в целое число, чтобы вы могли добавить его к другим. Затем просто перейдите к шагам, указанным для расчета вашего числа.
Все, что вам нужно сделать для его реализации, это правильно отобразить положения, указанные в инструкции к idx
позиция индекса в вашем строковом представлении числа. Просто сделайте это на бумаге с подсчетом в голове или используйте отрицательный idx (он считается с конца строки) в Ruby.
РЕДАКТИРОВАТЬ:
Решение может быть что-то вроде этого:
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 до бесконечности, для каждого элемента
Теперь у нас должно быть что-то вроде этого:
[[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[1].even?
- проверить, является ли позиция четнойi[1].even? ? 3 : 1
- для четного положения используйте 3, для противоположного (нечетного) используйте только 1reduce(:+)
- уменьшить результирующий массив до единого значения с помощью операции + (добавить все результаты)
Теперь самое интересное:-)
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
Это просто мёртвое простое решение, объяснять не стоит, если только кто-то не попросит его
Передайте свой номер указанному ниже методу, и он вернет число, к которому добавлена цифра контрольной суммы. Ссылка использована с: 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