Luhn 算法

Luhn算法
Luhn 算法或是Luhn 公式,也被称作“模10算法”。它是一种简单的校验公式,一般会被用于身份证号码,IMEI号码,美国供应商识别号码,或是加拿大的社会保险号码的验证。该算法是由IBM的科学家Hans Peter Luhn所创造,于1954年1月6日提出该专利的申请,并于1960年8月23日被授予,在美国的专利号为2950048。
该算法一直都被大家所公用,并且时至今日应用也很广泛。它被指定在ISO/IEC7812-1。它的目的不是成为一种加密安全的哈希函数;它的目的是防止意外出现的错误,而不是恶意攻击。很多信用卡和众多的政府身份识别号码都使用该算法从一系列的随机数字中提取有效的数字。
优点和缺点
Luhn 算法会检测到任何单码的错误以及几乎所有的相邻数字换位的错误。但是它不会检测两个数字序列09转90的错误(反之亦然)。它会检测到十分之七的相同双位数错误(不会检测到22和55的互换,33和66的互换,44和77的互换)。其他更复杂的检查数字算法,如费尔赫夫算法,可以检测出更多的转录错误。模N的Luhn算法是Luhn算法的一个扩展,支持非数字字符串。因为该算法采取了从右向左的方式,而且零位会影响计算的结果。只有当零位造成了数位的移动或是用零来填充一串数字的开头时才不会影响计算结果的生成。因此不论在将1234用零填充为0001234之前或是之后,使用Luhn算法得到的结果都是一样的。
该算法在美国专利上是为了给手持或是机械设备计算校验码。所以它必须尽可能的简单。

非正式的解释
该公式会通过校验码对一串数字进行验证。校验码通常会被加到账户号码中,从而拼合成一个完整的账户号码。拼合后的账户号码要通过以下的测试:
1.从校验位开始计数(校验位一般添加在账户的最后面),按从右向左的顺序,将偶数都乘以2.
2.将得到的结果相加起来(例如:10=1+0=1,14=1+4=5,也有的说法是若是乘2的结果是两位数的话,那么就直接减去9,和之前位数拆开相加的结果是一样的),然后再与原数字串的奇数位相加。
3.如果加起来的和模10后为0(也就是相加的结果是以0结尾的,10的倍数),那么这个数字串根据Luhn算法来说就是有效的,反之就是无效的。
假设一个字符串为“7992739871”,我们为其加上一个校验位,最后组成的数字为7992739871x:
账户号码:       7   9   9   2   7   3   9   8   7   1   x
将偶数位乘以2:7   18 9   4   7   6   9   16 7   2   x
相加后的数字: 7   9   9   4   7   6   9   7   7   2 =67
校验码x是通过将相加后的数字乘以9后,在进行模10计算(那么就是:(67*9)mod10,也有的说法是取比相加的和最小的10的整数倍数字,其实结果都是一样的)。通俗地说:
1.计算所有位数的和(67)。
2.将其乘以9(603)。
3.取最后一位数字(3)。
4.得到的结果就是校验位。

另外一种得到校验位的方法:先计算所有位数的和,用10减去所有位数和模10的结果。(67的个位是7;10-7=3即为校验位)。通俗地说:
1.计算所有位数的和(67)。
2.取个位数(7)。
3.用10减去个位数(3)。
4.得到的结果就是校验位。


这样,我们得到的完整的账户号码是:7992739871x。
下面的每一个数字 79927398710, 79927398711, 79927398712, 79927398713, 79927398714, 79927398715, 79927398716, 79927398717, 79927398718, 79927398719都给以用如下的方法进行验证。
1.从最右边开始计算,将偶数位都乘以2:(1*2)=2,(8*2)=16,(3*2)=6,(2*2)=4,(9*2)=18
2.将每一位数字加起来:x(校验位)+(2)+7+(1+6)+9+(6)+7+(4)+9+(1+8)+7=X+67.
3.如果得到的结果是10的倍数,那么这个账户号码就可能是有效的。需要注意的是3就是唯一的可以使得和(67+x)是10的整数倍的个位数。
4.因此,以上的所有账户除了79927398713 是有效的以外,其他均为无效的账户。

校验位的验证的代码实现
以下通过Python来实现的:

def luhn_checksum(card_number):
      def digits_of(n):
            return [int(d) for d in str(n)]
      digits = digits_of(card_number)
      odd_digits = digits[-1::-2]
      even_digits = digits[-2::-2]
      checksum = 0
      checksum += sum(odd_digits)
      for d in even_digits:
            checksum += sum(digits_of(d*2))
      return checksum % 10
 
def is_luhn_valid(card_number):
      return luhn_checksum(card_number) == 0

校验位的计算
上面的算法检查输入校验位的有效性。计算校验位需要一个小的适应算法,即:
1.切换奇/偶乘法。
2.如果得到的和(sum)模10等于0的话,那么校验码就是0。
3.否则,校验码就等于10减去得到的和模10(10 - (sum mod 10))

def calculate_luhn(partial_card_number):
      return 10 - luhn_checksum(int(partial_card_number) * 10)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值