生成 Luhn 校验和

问题描述 投票:0回答:9

验证 Luhn 校验和的实现有很多,但生成它们的实现却很少。我遇到过这个,但是在我的测试中它显示出有问题,而且我不理解 delta 变量背后的逻辑。

我已经制作了这个函数,据说应该生成 Luhn 校验和,但由于某种原因,我还不明白生成的校验和有一半时间是无效的。

function Luhn($number, $iterations = 1)
{
    while ($iterations-- >= 1)
    {
        $stack = 0;
        $parity = strlen($number) % 2;
        $number = str_split($number, 1);

        foreach ($number as $key => $value)
        {
            if ($key % 2 == $parity)
            {
                $value *= 2;

                if ($value > 9)
                {
                    $value -= 9;
                }
            }

            $stack += $value;
        }

        $stack = 10 - $stack % 10;

        if ($stack == 10)
        {
            $stack = 0;
        }

        $number[] = $stack;
    }

    return implode('', $number);
}

一些例子:

Luhn(3); // 37, invalid
Luhn(37); // 372, valid
Luhn(372); // 3728, invalid
Luhn(3728); // 37283, valid
Luhn(37283); // 372837, invalid
Luhn(372837); // 3728375, valid

我正在验证生成的校验和针对此页面,我在这里做错了什么?


为了将来参考,这里是工作函数。

function Luhn($number, $iterations = 1)
{
    while ($iterations-- >= 1)
    {
        $stack = 0;
        $number = str_split(strrev($number), 1);

        foreach ($number as $key => $value)
        {
            if ($key % 2 == 0)
            {
                $value = array_sum(str_split($value * 2, 1));
            }

            $stack += $value;
        }

        $stack %= 10;

        if ($stack != 0)
        {
            $stack -= 10;
        }

        $number = implode('', array_reverse($number)) . abs($stack);
    }

    return $number;
}

我删除了 $parity 变量,因为我们不需要它用于此目的,并验证:

function Luhn_Verify($number, $iterations = 1)
{
    $result = substr($number, 0, - $iterations);

    if (Luhn($result, $iterations) == $number)
    {
        return $result;
    }

    return false;
}
php algorithm checksum check-digit luhn
9个回答
10
投票

编辑:抱歉,我现在意识到您几乎已经有了我的全部答案,您只是错误地确定了哪个数字用于哪个因素。

我现在的整个回答可以用这一句话来概括:

你把因子颠倒了,你根据数字的长度将错误的数字乘以 2。


查看维基百科关于 Luhn 算法的文章

您的校验和一半时间无效的原因是,在您的检查中,您的号码有一半时间为奇数位,然后您将错误的数字加倍。

对于 37283,从右边数起,您会得到以下数字序列:

  3 * 1 =  3             3
  8 * 2 = 16 --> 1 + 6 = 7
  2 * 1 =  2             2
  7 * 2 = 14 --> 1 + 4 = 5
+ 3 * 1 =  3             3
=                       20

该算法要求您将原始数字中的各个数字以及“从右侧开始的每两位数字”的乘积的各个数字相加。

因此,从右边开始,将 3 + (1 + 6) + 2 + (1 + 4) + 3 相加,得到 20。

如果您最终得到的数字以零结尾(20 就是这样),则该数字是有效的。

现在,您的问题暗示您想知道如何生成校验和,嗯,这很简单,请执行以下操作:

  1. 多加一个零,这样你的数字就会从 xyxyxyxy 变为 xyxyxyxy0
  2. 计算新数字的 luhn 校验和总和
  3. 求和,模 10,这样你就得到 0 到 10 之间的一位数
  4. 如果数字是 0,那么恭喜,您的校验和数字是零
  5. 否则,计算 10 位数字以获得最后一位数字所需的内容,而不是零

例如:号码是12345

  1. 加零:123450
  2. 计算 123450 的 luhn 校验和,结果为

    0   5    4    3    2    1
    1   2    1    2    1    2  <-- factor
    0   10   4    6    2    2  <-- product
    0  1 0   4    6    2    2  <-- sum these to: 0+1+0+4+6+2+2=15
    
  3. 求和 (15),模 10,得到 5

  4. 数字(5)不为零
  5. 计算 10-5,结果是 5,最后一位数字应该是 5。

所以结果是123455。


3
投票

你的 php 有 bug,它会导致无限循环。 这是我正在使用的工作版本,根据您的代码进行了修改

函数 Luhn($number) {

$stack = 0;
$number = str_split(strrev($number));

foreach ($number as $key => $value)
{
    if ($key % 2 == 0)
    {
        $value = array_sum(str_split($value * 2));
    }
    $stack += $value;
}
$stack %= 10;

if ($stack != 0)
{
    $stack -= 10;     $stack = abs($stack);
}


$number = implode('', array_reverse($number));
$number = $number . strval($stack);

return $number; 

}

创建一个 php 并在本地主机 Luhn(xxxxxxxx) 中运行以确认。


3
投票

不好

我简直不敢相信有多少糟糕的实现。

IDAutomation 有一个带有 MOD10() 函数的 .NET 程序集可供创建,但它似乎不起作用。在 Reflector 中,代码对于它应该做的事情来说太长了。


不好

这个混乱的页面实际上当前从维基百科(!)链接到Javascript,它有几个验证实现,当我调用每个验证实现时,它们甚至不返回相同的值。


从维基百科的 Luhn 页面链接到的

页面有一个 Javascript 编码器,似乎可以工作:

// Javascript String.prototype.luhnGet = function() { var luhnArr = [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8,1,3,5,7,9]], sum = 0; this.replace(/\D+/g,"").replace(/[\d]/g, function(c, p, o){ sum += luhnArr[ (o.length-p)&1 ][ parseInt(c,10) ] }); return this + ((10 - sum%10)%10); }; alert("54511187504546384725".luhnGet());​


这个

非常有用的EE4253页面验证了校验位,还显示了完整的计算和解释。


我需要 C# 代码,最终使用了这个

code 项目代码:

// C# public static int GetMod10Digit(string data) { int sum = 0; bool odd = true; for (int i = data.Length - 1; i >= 0; i--) { if (odd == true) { int tSum = Convert.ToInt32(data[i].ToString()) * 2; if (tSum >= 10) { string tData = tSum.ToString(); tSum = Convert.ToInt32(tData[0].ToString()) + Convert.ToInt32(tData[1].ToString()); } sum += tSum; } else sum += Convert.ToInt32(data[i].ToString()); odd = !odd; } int result = (((sum / 10) + 1) * 10) - sum; return result % 10; }


这个 C# 中的验证代码

似乎可以工作,尽管有点笨拙。我只是用它来检查上面是否正确。


1
投票

function isLuhnValid($number) { if (empty($number)) return false; $_j = 0; $_base = str_split($number); $_sum = array_pop($_base); while (($_actual = array_pop($_base)) !== null) { if ($_j % 2 == 0) { $_actual *= 2; if ($_actual > 9) $_actual -= 9; } $_j++; $_sum += $_actual; } return $_sum % 10 === 0; }



0
投票

https://github.com/xi-project/xi-algorithm

Packagist 也有售


0
投票

/// <summary> /// Calculates Luhn Check Digit based on /// https://en.wikipedia.org/wiki/Luhn_algorithm /// </summary> /// <param name="digits">The digits EXCLUDING the check digit on the end. /// The check digit should be compared against the result of this method. /// </param> /// <returns>The correct checkDigit</returns> public static int CalculateLuhnCheckDigit(int[] digits) { int sum = 0; bool isMultiplyByTwo = false; //Start the summing going right to left for (int index = digits.Length-1; index >= 0; --index) { int digit = digits[index]; //Every other digit should be multipled by two. if (isMultiplyByTwo) digit *= 2; //When the digit becomes 2 digits (due to digit*2), //we add the two digits together. if (digit > 9) digit = digit.ToString() .Sum(character => (int)char.GetNumericValue(character)); sum += digit; isMultiplyByTwo = !isMultiplyByTwo; } int remainder = sum % 10; //If theres no remainder, the checkDigit is 0. int checkDigit = 0; //Otherwise, the checkDigit is the number that gets to the next 10 if (remainder != 0) checkDigit = 10 - (sum % 10); return checkDigit; }

其使用示例:

public static bool IsValid(string userValue) { //Get the check digit from the end of the value int checkDigit = (int)char.GetNumericValue(userValue[userValue.Length - 1]); //Remove the checkDigit for the luhn calculation userValue = userValue.Substring(0, userValue.Length - 1); int[] userValueDigits = userValue.Select(ch => (int)char.GetNumericValue(ch)) .ToArray(); int originalLuhnDigit = CalculateLuhnCheckDigit(userValueDigits); //If the user entered check digit matches the calcuated one, //the number is valid. return checkDigit == originalLuhnDigit; }



0
投票

试试这个:

<?php function Luhn($digits) { $sum = 0; foreach (str_split(strrev($digits)) as $i => $digit) { $sum += ($i % 2 == 0) ? array_sum(str_split($digit * 2)) : $digit; } return $digits . (10 - ($sum % 10)) % 10; }

将 Luhn 校验和添加到 $input 

$digits = Luhn($input);

使用 Luhn 校验和验证数字:

if ($digits == Luhn(substr($digits, 0, -1))) { // ... }

获取校验和号码:

$luhn_digit = substr(Luhn($digits), -1);



0
投票


0
投票

function isValdLuhn($str) { $sum = 0; $isSecond = false; for ($i = strlen($str); $i >= 1; $i--) { $num = $i - 1; if($isSecond) { $a = $str[$num] * 2; $sum += intdiv($a, 10) + $a%10; } else { $sum += $str[$num]; } $isSecond = !$isSecond; } return $sum % 10 === 0 ? 'VALID' : 'INVALID'; } isValdLuhn('8174060176912597');

© www.soinside.com 2019 - 2024. All rights reserved.