Грубо говоря, если у нас есть какой-то длинный идентификатор (код) из цифр, мы можем сделать для него контрольный разряд. Делается так:
1) Берём каждую цифру из кода по очереди, начиная с самой правой
2) Суммируем эти цифры, но каждый второй элемент предварительно умножаем на 2, начиная с самого правого. Так, если есть последовательность "1234", то выходит что-то в духе "4 * 2 + 3 + 2 * 2 + 1".
3) Если в результате умножения на 2 вышло число > 10, то просто суммируем его разряды (к примеру, если 14, то делаем 1 + 4)
4) Получившийся результат R используем в формуле (R mod 10) - или аналогичной, там их много
5) Итог и будет контрольным разрядом, который дописывается в конец кода
6) Для проверки корректности этого разряда достаточно применить операцию к коду ещё раз (предварительно отбросив код)
К примеру, имея код
"1369"
получаем
(9 * 2) + 6 + (3 * 2) + 1
18 + 6 + 6 + 1
(1 + 8) + 6 + 6 + 1
22
(22 mod 10) = 2
2 и будет контрольным битом для всей свёртки. Забавно, так как подобный алгоритм позволяет защитить от некоторых типичных опечаток при вводе кодов, номеров кредиток и прочего, хотя, к сожалению, не от всех.
Можно написать простую реализацию (хотя и наивную)
class Luhn
def add_validation_to(code)
"#{code}#{check_digit_for(code)}"
end
def valid?(code)
code_chars = code.to_s.chars
check_digit_for(code_chars[0...-1]) == code_chars[-1].to_i
end
private
def check_digit_for(code)
digest = code.to_s.chars.reverse.map(&:to_i).inject(0) do |res, num|
res + num.divmod(10).sum
end
digest % 10
end
end
luhn = Luhn.new
validated = luhn.add_validation_to('12345')
puts luhn.valid?(validated)