Реализации алгоритмов/Алгоритм Луна
Внешний вид
Алгоритм Лу́на (англ. Luhn algorithm) — алгоритм вычисления контрольной цифры номера пластиковых карт в соответствии со стандартом ISO/IEC 7812.
Num[1..N] — номер карты, Num[N] — контрольная цифра.
sum = 0
for i = 1 to N-1 do
p = Num[N-i]
if (i mod 2 <> 0) then
p = 2*p
if (p > 9) then
p = p - 9
end if
end if
sum = sum + p
next i
//дополнение до 10
sum = 10 - (sum mod 10)
if (sum == 10) then
sum = 0
end if
Num[N] = sum
private static boolean isValidLuhn(String value) {
int sum = Character.getNumericValue(value.charAt(value.length() - 1));
int parity = value.length() % 2;
for (int i = value.length() - 2; i >= 0; i--) {
int summand = Character.getNumericValue(value.charAt(i));
if (i % 2 == parity) {
int product = summand * 2;
summand = (product > 9) ? (product - 9) : product;
}
sum += summand;
}
return (sum % 10) == 0;
}
#include <string.h> // заголовок, объявляющий функцию strlen()
int luhn(const char * card_number) // принимаем в аргументы номер карты
{
int len = strlen(card_number); // узнаем длину номера карты
int digit = 0; // текущая цифра в цикле (см. ниже)
int check_digit = 0; // переменная, которая будет хранить проверочную цифру
int i;
for (i = len - 1; i >= 0; --i) // главный цикл, в процессе него вычисляется проверочная цифра
{
digit = card_number[i] - '0'; // переводим цифру из char в int
if (i % 2 == 0) // если позиция цифры чётная, то:
{
digit *= 2; // умножаем цифру на 2
if (digit > 9) // согласно алгоритму, ни одно число не должно быть больше 9
digit -= 9; // второй вариант сведения к единичному разряду
}
check_digit += digit; // прибавляем к check_digit номера согласно алгоритму
}
return check_digit % 10; // возвращаем проверочное число, вычисленное согласно алгоритму
}
#include <string>
int luhn(std::string const & input)
{
int check_digit = 0;
bool odd = false;
for (auto it = input.rbegin(); it != input.rend(); ++it)
{
auto digit = *it - '0';
if ((odd = !odd))
{
digit *= 2;
if (digit > 9)
digit -= 9;
}
check_digit += digit;
}
return (check_digit * 9) % 10;
}
// Алгоритм не корректен при номере состоящем из 19 цифр. Например: 3538953650304290005
bool IsValidLuhn(in int[] digits)
{
int check_digit = 0;
for (int i = 0; i < digits.Length; ++i)
check_digit += ((i & 1) is 0) switch
{
true => digits[i] > 4 ? digits[i] * 2 - 9 : digits[i] * 2,
false => digits[i]
};
return check_digit % 10 == 0;
}
Правильная реализация
bool AlgorithmLuhn(int[] cardNumbers)
{
var sum = 0;
for (var i = 0; i < cardNumbers.Length; i++)
{
sum += cardNumbers.Length % 2 == 0
? (cardNumbers.Length - i) % 2 == 0
? cardNumbers[i]
: cardNumbers[i] > 4
? cardNumbers[i] * 2 - 9
: cardNumbers[i] * 2
: (cardNumbers.Length - i) % 2 != 0
? cardNumbers[i]
: cardNumbers[i] > 4
? cardNumbers[i] * 2 - 9
: cardNumbers[i] * 2;
}
return sum % 10 == 0;
}
func Valid(number int) bool {
return (number%10+checksum(number/10))%10 == 0
}
func checksum(number int) int {
var luhn int
for i := 0; number > 0; i++ {
cur := number % 10
if i%2 == 0 { // even
cur = cur * 2
if cur > 9 {
cur = cur%10 + cur/10
}
}
luhn += cur
number = number / 10
}
return luhn % 10
}
from functools import reduce
def luhn(code):
# Предварительно рассчитанные результаты умножения на 2 с вычетом 9 для больших цифр
# Номер индекса равен числу, над которым проводится операция
LOOKUP = (0, 2, 4, 6, 8, 1, 3, 5, 7, 9)
code = reduce(str.__add__, filter(str.isdigit, code))
evens = sum(int(i) for i in code[-1::-2])
odds = sum(LOOKUP[int(i)] for i in code[-2::-2])
return ((evens + odds) % 10 == 0)
print("Прошел проверку: ", luhn('4561 2612 1234 5467'))
print("Не прошел проверку: ", luhn('4561 2612 1234 5464'))
function luhnAlgorithm(value) {
value = value.replace(/\D/g, '');
var nCheck = 0;
var bEven = false;
for (var n = value.length - 1; n >= 0; n--) {
var nDigit = parseInt(value.charAt(n), 10);
if (bEven && (nDigit *= 2) > 9) {
nDigit -= 9;
}
nCheck += nDigit;
bEven = !bEven;
}
return (nCheck % 10) == 0;
}
Вариант покороче
const Moon_Algorithm = setValue => {
let ch = 0;
const num = String(setValue).replace(/\D/g, '');
const isOdd = num.length % 2 !== 0;
if ('' === num) return false;
for (let i = 0; i < num.length; i++) {
let n = parseInt(num[i], 10);
ch += (isOdd | 0) === (i % 2) && 9 < (n *= 2) ? (n - 9) : n;
}
return 0 === (ch % 10);
};
function luhnAlgorithm($digit)
{
$number = strrev(preg_replace('/[^\d]+/', '', $digit));
$sum = 0;
for ($i = 0, $j = strlen($number); $i < $j; $i++) {
if ((($i + 1) % 2) == 0) {
$val = $number[$i];
} else {
$val = $number[$i] * 2;
if ($val > 9) {
$val -= 9;
}
}
$sum += $val;
}
return (($sum % 10) === 0);
}
fun String.luhnAlgorithm() = reversed()
.map(Character::getNumericValue)
.mapIndexed { index, digit ->
when {
index % 2 == 0 -> digit
digit < 5 -> digit * 2
else -> digit * 2 - 9
}
}.sum() % 10 == 0
set serveroutput on;
declare
vpan varchar2(50) := '2345698465';
x varchar2(2):=0;
s varchar2(3):=0;
begin
for i in 1..length(vpan)
loop
x:= substr(vpan,length(vpan)-i+1,1);
if mod(i,2) != 0 then x:=x*2; if x>9 then x:=x-9; end if; end if;
s:= s+x;
end loop;
s:=10-mod(s,10);
if s = 10 then s:=0; end if;
dbms_output.put_line('luhn= '||s||' card= '||vpan||s);
end;
var Moon_Algorithm: any = (setValue: any): boolean => {
var ch: number = 0,
num: any = String(setValue).replace(/\D/g, '');
if ('' === num) return false;
for (var i in num) {
var n: number = parseInt(num[i], 10);
ch += 0 === (i % 2) && 9 < (n *= 2) ? (n - 9) : n;
}
return 0 == (ch % 10);
}
Паскаль
[править]Внимание! (Возможно не верная реализация, результат не совпадает с результатами реализаций на сайте https://rosettacode.org/wiki/Luhn_test_of_credit_card_numbers) |
Num[1..N] — номер карты, Num[N] — контрольная цифра.
function Luhn(Num: array of Integer; var sum: Integer): Boolean;
var
i, p: integer;
begin
sum := 0;
for i := High(Num) downto Low(Num) do
begin
p := Num[i];
if i mod 2 = 0 then
p := 2 * p;
if p > 9 then
p := p - 9;
sum := sum + p;
end;
Result := sum mod 10 = 0;
sum := 10 - (sum mod 10);
if sum = 10 then
sum := 0;
end;
1С
[править]Оригинальный алгоритм, описанный Луном
Функция АлгоритмЛуна(Текст)
ДлинаНомера = СтрДлина(Текст);
Сумма = 0;
Для i = 1 по ДлинаНомера Цикл
НомерЦифры = ДлинаНомера-i+1;
Цифра = Число(Сред(Текст,НомерЦифры,1));
Сумма = Сумма+?(НомерЦифры%2=1,Цифра,Цифра*2%9);
КонецЦикла;
Возврат Сумма%10 = 0;
КонецФункции
Общепринятый алгоритм
&НаСервере
Функция ПроверкаНомераКарты(Текст) Экспорт
ДлинаНомера = СтрДлина(Текст);
Сумма = 0;
Если ДлинаНомера%2=0 Тогда //четная карта
Для i = 0 по ДлинаНомера-1 Цикл
НомерЦифры = i+1;
Цифра = Число(Сред(Текст,НомерЦифры,1));
Сумма = Сумма+?(НомерЦифры%2=1,?(Цифра*2>9,Цифра*2-9,Цифра*2),Цифра);
КонецЦикла;
иначе
Для i = 0 по ДлинаНомера-1 Цикл
НомерЦифры = i+1;
Цифра = Число(Сред(Текст,НомерЦифры,1));
Сумма = Сумма+?(НомерЦифры%2=0,?(Цифра*2>9,Цифра*2-9,Цифра*2),Цифра);
КонецЦикла;
КонецЕсли;
Возврат Сумма%10 = 0
КонецФункции
Ruby
[править]Выражение возвращает true если число (num) соответствует алгоритму Луна и false в противном случае.
num.to_s
.reverse
.split(//)
.each_slice(2)
.flat_map{|a,b| [a.to_i,2*b.to_i]}
.join
.split(//)
.map(&:to_i)
.reduce(:+)%10==0