【问题标题】:Generating Luhn Checksums生成 Luhn 校验和
【发布时间】:2009-09-13 22:04:06
【问题描述】:

验证 Luhn 校验和的实现有很多,但生成它们的实现却很少。我遇到过this one,但是在我的测试中发现它有问题,我不明白 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

我正在验证生成的校验和 against this page,我在这里做错了什么?


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

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;
}

【问题讨论】:

  • 顺便说一句,验证校验和与生成它并检查它是否为零是相同的 - 所以所有这些“验证”例程也可以用来生成。
  • @Nick:是的,但这涉及检查 10 个不同的数字,我更喜欢相反的方式(使用 generate 函数验证)。
  • 什么?不,您只需“验证”附加 0 的数字,然后将最后一位数字替换为 (9-result)。
  • 对不起,你说得对,我误解了你的说法。
  • 到目前为止,我只发现您在 PHP 中的 Luhn 生成代码实现是可用的。我认为如果你用它在 github 中创建一个简单的存储库会很有趣。你怎么看?

标签: php algorithm checksum check-digit luhn


【解决方案1】:

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

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

您的因子颠倒了,您将错误的数字乘以 2,具体取决于数字的长度。


看看Wikipedia article on the Luhn algorithm

你的校验和一半时间无效的原因是你的支票有一半时间你的号码有奇数位数,然后你加倍了错误的数字。

对于 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。

【讨论】:

  • 谢谢,我意识到在这种情况下我最右边的数字总是我的最后一个数字(因为我正在生成校验和数字)。
【解决方案2】:

你的 php 有问题,它会导致无限循环。 这是我正在使用的工作版本,从您的代码修改

函数 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 有一个 .NET assembly with a MOD10() function 要创建,但它似乎不起作用。在 Reflector 中,代码对于它应该做的事情来说太长了。


    不好

    This mess of a page 实际上当前链接到 Wikipedia(!) for Javascript 有几个验证实现,当我调用每个验证实现时它们甚至不返回相同的值。


    page linked to from Wikipedia's Luhn page 有一个似乎可以工作的 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());​
    

    very useful EE4253 页面验证校验位并显示完整的计算和解释。


    我需要 C# 代码并最终使用了这个code project 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;
            }
    

    这个validation code in C# 似乎可以工作,虽然有点笨拙。我只是用它来检查以上是否正确。

    【讨论】:

    • 我实际上最终花了 方式 太多时间试图找到工作代码,应该自己写。请记住,验证算法和检查算法基本相同 - 通过验证,您只需为字符串中的 n-1 位数字创建校验和并与最后一位数字进行比较
    • 当 sum % 10 == 0 时,您的 c# 答案不会返回正确的结果,“c# 中的验证码”链接也已失效。
    【解决方案4】:

    现在有一个基于原始问题/答案的 github 存储库。见

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

    packagist 也可以买到

    【讨论】:

      【解决方案5】:

      这是一个可以帮助你的功能,它很短,而且效果很好。

      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;
      }
      

      【讨论】:

        【解决方案6】:

        由于显示或链接到 C# 的其他答案不起作用,我添加了一个经过测试且解释性更强的 C# 版本:

            /// <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;
            }
        

        【讨论】:

          【解决方案7】:

          奇偶校验必须从右边开始。

          试试这个:

          <?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);
          

          【讨论】:

            猜你喜欢
            • 2018-02-18
            • 2011-08-17
            • 1970-01-01
            • 2018-06-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-07-02
            相关资源
            最近更新 更多