我同意你的看法;维基百科上的定义不是我见过的最清楚的。
在两个示例之前有一段文字解释了何时应该使用一种或另一种算法:
由于 NSIN 元素可以是任何字母数字序列(9 个字符),因此奇数个字母将产生偶数个数字,而偶数个字母将产生奇数个数字。对于奇数位数,使用第一个示例中的方法。对于偶数位数,使用第二个例子中的方法
NSIN 与 ISIN 相同,除了前两个字母和最后一个数字;因此,如果 ISIN 为 US0378331005,则 NSIN 为 037833100。
因此,如果要验证US0378331005 的校验和数字,则必须使用“第一个算法”,因为 NSIN 中有 9 位数字。相反,如果您想检查AU0000XVGZA3,您将使用“第二种算法”,因为 NSIN 包含 4 位数字。
至于“第一”和“第二”算法,它们是相同的,唯一的例外是前者将奇数组乘以 2,而后者将乘以 2偶数组。
现在,好消息是,您可以摆脱这种过于复杂的算法。
您可以改为:
- 输入 ISIN,除了最后一位(您需要验证)
- 将所有字母转换为数字,从而获得数字列表
- 反转数字列表
- 如果结果 >= 10,则奇数位置的所有数字都加倍,并且它们的数字再次相加
- 偶数位置的所有数字均按原样获取
- 对所有数字求和,取模,从 0 中减去结果,取绝对值
唯一棘手的步骤是#4。让我们用一个小例子来澄清它。
假设奇数位的数字是4, 0, 7。
你将它们加倍并得到:8, 0, 14。
8 不是 >= 10,所以我们照原样取它。 0 同上。14 是 >= 10,所以我们再次对其数字求和:1+4=5。
因此,此小示例中第 4 步的结果是:8, 0, 5。
在 Python 中的最小工作实现可能如下所示:
import string
def digit_sum(n):
return (n // 10) + (n % 10)
alphabet = {letter: value for (value, letter) in
enumerate(''.join(str(n) for n in range(10)) + string.uppercase)}
isin_to_digits = ''.join(str(d) for d in (alphabet[v] for v in isin[:-1]))
isin_sum = 0
for (i, c) in enumerate(reversed(isin_to_digits), 1):
if i % 2 == 1:
isin_sum += digit_sum(2*int(c))
else:
isin_sum += int(c)
checksum_digit = abs(- isin_sum % 10)
assert int(isin[-1]) == checksum_digit
或者,更挤,只是为了功能的乐趣:
checksum_digit = abs( - sum(digit_sum(2*int(c)) if i % 2 == 1 else int(c)
for (i, c) in enumerate(
reversed(''.join(str(d) for d in (alphabet[v] for v in isin[:-1]))), 1)) % 10)