Как производится расчет контрольной цифры в Ruby? - PullRequest
1 голос
/ 29 августа 2011

Я пытаюсь построить расчет контрольных цифр в Ruby для номеров отслеживания FedEx.

Вот информация и шаги для вычисления контрольной цифры:

  • Цифровые позиции обозначены справа налево.
  • Цифра 1 - символ проверки.
  • Цифры с 16 по 22 не используются.

Шаги:

  1. Начиная с позиции 2, сложите значения четных пронумерованных позиций.
  2. Умножьте результаты первого шага на три.
  3. Начиная с позиции 3, сложите значения нечетных позиций. Помните - позиция 1 - это контрольная цифра, которую вы пытаетесь вычислить.
  4. Добавьте результат второго шага к результату третьего шага.
  5. Определите наименьшее число, которое при добавлении к номеру из шага 4 дает кратное 10. Это контрольная цифра.

Вот пример процесса (предоставлен FedEx): enter image description here

Итак, как мне реализовать это в Ruby?

1 Ответ

5 голосов
/ 29 августа 2011

Если у вас есть ваш номер в виде строки (или если у вас есть цифра в виде целого числа, просто наберите #to_s и получите строку), а затем вы можете просто извлечь цифры из него с помощью:

number_string[idx].to_i

или если вы используете Ruby 1.8

number_string[idx..idx].to_i

#to_i - преобразовать его в целое, чтобы вы могли добавить его к другим. Затем просто выполните шаги для расчета вашего числа.

Все, что вам нужно сделать для его реализации, это правильно отобразить позиции, указанные в инструкции, на idx индексную позицию в вашем строковом представлении числа. Просто сделайте это на бумаге с подсчетом в голове или используйте отрицательный idx (отсчитывается от конца строки) в Ruby.

EDIT

Решение может быть примерно таким:

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, для противоположного (нечетного) используйте только 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

Это просто мёртвое простое решение, объяснять не стоит, если только кто-то не попросит об этом

...