Перейти к содержанию

Реализации алгоритмов/Алгоритм Луна

Материал из Викиучебника — открытых книг для открытого мира

Алгоритм Лу́на (англ. 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);
                }

Паскаль

[править]

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;

Оригинальный алгоритм, описанный Луном

 Функция АлгоритмЛуна(Текст)

 	ДлинаНомера = СтрДлина(Текст);
 	Сумма = 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

КонецФункции

Выражение возвращает 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