Fact-checked by Grok 2 weeks ago

Verhoeff algorithm

The Verhoeff algorithm is a formula for error detection in sequences of decimal digits, developed by Dutch mathematician Jacobus Verhoeff and first published in 1969 as part of his work on . It computes a check digit appended to a number to verify its integrity, employing multiplication and permutation tables derived from the of order 10 (D5) to ensure robust detection of common input errors. Unlike simpler methods such as the , the Verhoeff algorithm detects all single-digit errors, all adjacent transpositions, all twin errors (replacing two adjacent digits with another pair), all jump transpositions, and all jump twin errors, making it particularly effective against typical clerical or typing mistakes. Verhoeff's innovation stemmed from his doctoral dissertation on , conducted while studying in and his role at the Mathematical Centre in , where he explored -correcting codes over arbitrary alphabets, with a focus on systems for practical applications like numbers and account codes. The algorithm's mathematical foundation relies on permutations of digits and multiplications in the of order 10, processed iteratively to compute the 10 and avoid false positives for detected types; this group structure ensures that erroneous digits alter the in detectable ways. Published in the Error Detecting Codes, it represented the first systematic scheme capable of such comprehensive single-check-digit protection, influencing subsequent -detection standards despite its relative obscurity outside specialized fields. Although not as widely adopted as the in consumer applications like credit cards, the Verhoeff method has found use in high-reliability contexts, such as the Indian identification system and software implementations for verification in programming libraries. Its superior error-detection rate—up to 100% for specified error classes—positions it as a benchmark in , with modern analyses confirming no equivalent length-3 or length-4 decimal code matches its properties using a single . Verhoeff (1927–2018), who later became a professor at , also contributed to mathematical art and , but his 1969 work remains his most cited achievement in .

History and Overview

Development and Inventor

The Verhoeff algorithm was invented by Jacobus "Koos" Verhoeff (1927–2018), a and renowned for his contributions to and error detection. Born on February 20, 1927, in , Verhoeff studied mathematics at the universities of and , where he completed his doctoral dissertation on . His academic and professional career included positions at the Mathematical Centre in , the Technological University in , Philips Research Laboratories in , and as a full professor of computer science at , from which he retired. Verhoeff developed the algorithm in as part of his broader research into error-detecting codes for numerical data. It originated from his doctoral work and was first detailed in the publication Error Detecting Decimal Codes, Mathematical Centre Tract No. 29, issued by the Mathematical Centre in . This tract represented a reworked version of his thesis, systematically exploring decimal schemes to improve . The motivation for the algorithm stemmed from the rapid computerization of administrative and financial systems during the , an era when mechanical and increasingly handled large volumes of numerical information, such as in banking, clearing offices, and postal services. Verhoeff's research addressed the growing need for robust detection to mitigate common transcription mistakes and machine-related faults in these systems, drawing on empirical studies of real-life errors to design more reliable codes. For instance, applications were noted in contexts like the Amsterdam Municipal Clearing Office and the Postal-Check and Service, where accurate data was essential to prevent costly discrepancies.

Purpose and Error Detection Capabilities

The Verhoeff algorithm serves as a method to generate a single appended to numerical strings, enabling the verification of during transcription or transmission. This approach is particularly designed to safeguard against common human-induced errors in , such as those occurring in numbers or codes. By incorporating the check digit, the algorithm allows recipients to confirm whether the received sequence matches the original without requiring additional . At its core, the algorithm achieves complete detection of all single-digit errors, where any one digit is incorrectly altered, and all adjacent transposition errors, in which two neighboring digits are swapped. These capabilities stem from the underlying structure leveraging concepts from the , which ensures that such modifications result in an invalid . This makes it highly effective for preventing undetected mistakes in manual data handling. Beyond these, the Verhoeff algorithm detects approximately 95% of random burst errors involving up to two digits (twin errors) and about 94% of jump errors, such as transpositions separated by one or more positions. Compared to simpler modulo-10 check digit methods, which detect all single errors but fail to detect adjacent transpositions, the Verhoeff approach provides superior resistance to prevalent transcription pitfalls, including swaps and phonetic confusions, thereby enhancing overall error detection reliability in practical applications.

Mathematical Basis

Dihedral Group D_5

The group D_5 underlies the Verhoeff algorithm as the of the regular pentagon, consisting of 10 elements: five rotations by multiples of $72^\circ (including the ) and five reflections across axes of symmetry passing through a and the of the opposite side. This group, of order 10, captures the full set of isometries preserving the pentagon's structure. The group operation is the of these symmetries, which is associative but non-commutative; for instance, applying a followed by a generally yields a different result from the reverse order. In the context of the algorithm, D_5 is isomorphic to a specific group structure on the set of digits {0, 1, 2, ..., 9}, where elements correspond to permutations of these digits under defined rules that mimic the symmetries' actions. The choice of D_5 leverages its non-abelian nature to enhance error detection, particularly for transpositions of adjacent digits; in abelian groups, such swaps ab to ba would satisfy a \cdot b = b \cdot a, potentially evading detection, but D_5's non-commutativity ensures these yield distinct products, achieving 100% detection of single-digit errors and adjacent transpositions. This algebraic property provides a robust framework for constructing the algorithm's multiplication tables.

Permutation and Multiplication Tables

The Verhoeff algorithm employs two primary tables derived from the D_5 of order 10: a and a permutation table. These tables facilitate error detection by mapping decimal digits to group elements and leveraging the group's non-commutative structure. The D_5 consists of symmetries of a regular , including 5 rotations (by $0^\circ, 72^\circ, 144^\circ, 216^\circ, 288^\circ) and 5 reflections, providing a non-abelian operation that enhances detection of digit transpositions compared to commutative alternatives. Digits 0 through 9 are mapped to the elements of D_5 as follows: 0 represents the (neutral rotation), 1 through 4 represent the non-trivial (R, R^2, R^3, R^4), and 5 through 9 represent the (e.g., s, sR, sR^2, sR^3, sR^4, where s is a fixed reflection). This mapping ensures that the group operation corresponds to a "multiplication" of digits, with the resulting element relabeled as a digit from 0 to 9. The is the of D_5 under this labeling, capturing the group product for every pair of elements. For instance, the entry for 1 × 2 yields 3, reflecting the of rotations R \cdot R^2 = R^3. The table's non-commutativity (e.g., 1 × 5 = 6 but 5 × 1 = 9) is crucial for distinguishing error types. The multiplication table is presented below:
×0123456789
00123456789
11234067895
22340178956
33401289567
44012395678
55987604321
66598710432
77659821043
88765932104
99876543210
This table is constructed by computing all products in D_5 and mapping the results back to digits via the labeling scheme. The permutation table is a 10×10 array where each row corresponds to successive applications of a base \pi in the S_{10}, derived to ensure transposition detection. The base \pi is defined as \pi(0)=1, \pi(1)=5, \pi(2)=7, \pi(3)=6, \pi(4)=2, \pi(5)=8, \pi(6)=3, \pi(7)=0, \pi(8)=9, \pi(9)=4, with cycle structure (0 1 5 8 9 4 2 7)(3 6). This \pi is chosen such that \pi(d) \not\equiv d \pmod{10} for d \neq 0, and it cycles through digits to mix positions effectively. The entry p is then \pi^i(d), the result of applying \pi exactly i times to digit d, for i=0 to 9. This construction ensures the table's rows form powers of \pi, promoting and error sensitivity. The permutation table is:
i\d0123456789
00123456789
11576283094
25803796142
38916043527
49453126870
54286573901
62793806415
77046913258
80159480763
91576283094
An inverse table is also used in check digit computation, consisting of a 10-element array where each entry \mathrm{inv} is the digit e such that the group multiplication d \times e = 0 (the identity). This table is [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]. It allows finding the check digit that makes the total checksum 0. The properties of these tables collectively enable the algorithm to detect all single-digit errors and all adjacent transpositions (100%), as well as a high percentage (about 95%) of non-adjacent transpositions, owing to the interplay between the group's multiplication and the permutation's mixing.

Algorithm Mechanics

Check Digit Computation Steps

The computation of the check digit in the Verhoeff algorithm involves processing the input digits through a series of group operations defined by the permutation and multiplication tables derived from the D_5 of order 10. To begin, reverse the order of the input number's digits, labeling them as d_1, d_2, \dots, d_n, where d_1 is the original rightmost digit and d_n is the original leftmost digit. This aligns the positions with the algorithm's right-to-left processing convention, treating the future check digit position as 0. Initialize an accumulator c_0 = 0, representing the identity element in the group. For each position i from 1 to n, update the accumulator iteratively as follows: c_i = \text{multiplication_table} [c_{i-1}] \left[ \text{permutation_table}[i \mod 8][d_i] \right] This step performs the group multiplication of the current accumulator with the position-specific permutation applied to the digit d_i. The permutation table, of size 8×10 and repeating cyclically every 8 positions, ensures varied transformations across positions to enhance error detection, while the 10×10 multiplication table encodes the non-commutative operations of D_5. After all digits, the final accumulator value is c_n. The k is then determined by k = \text{inverse_table}[c_n], where the inverse table provides the digit k such that k * c_n = 0 (the ) in the group , ensuring that when k is prepended in the (as 0), the overall product is the . This selection ensures that appending k to the original number results in a complete codeword where the overall group product is the , satisfying the code's defining .

Validation Process

To validate a number using the Verhoeff algorithm, the complete sequence—including the as the rightmost d_0—is processed from right to left, applying the same and D_5 multiplication operations as in check digit computation. The process begins with an initial c_0 = 0 (the in D_5), and for each i from 0 to n, the checksum is updated as c_{i+1} = \text{multiplication_table} [c_i] [\text{permutation_table}[i \mod 8][d_i]], where d_i is the i-th from the right. The number is valid if the final c_{n+1} = 0. This validation differs from check digit generation by incorporating the existing check digit into the computation at position 0 and testing for the identity rather than solving for a specific value to append. The algorithm accommodates variable-length numbers, as the cyclic nature of the permutations (repeating every 8 positions) ensures consistent error detection regardless of length. Leading zeros are handled as standard digit 0, preserving the mathematical mapping without special preprocessing. The Verhoeff algorithm's design also enables potential error localization through partial checksum computations; by calculating intermediate values c_k for subsets of digits from either end, the position of a single-digit error or adjacent transposition can be identified where the partial sum deviates from expected values under the group operations.

Practical Implementation

Table-Driven Method

The table-driven method implements the Verhoeff algorithm by precomputing lookup tables that define the check digit operations, allowing for efficient computation without repeated derivations of group elements. These tables include representations of permutations applied to digits based on their position from the right, multiplication operations within the dihedral group D_5, and inverses for validation, each structured as 10×10 arrays corresponding to the decimal digits 0 through 9. The tables are generated once and stored for reuse across multiple calculations, ensuring the core steps—such as iteratively multiplying permuted digits—rely solely on array lookups rather than algorithmic generation of permutations or group multiplications. This precomputation enables the algorithm to process an n-digit number in linear O(n) , with only constant space overhead for the fixed-size s, making it highly efficient for repeated use in detection tasks. By substituting complex on-the-fly group operations with simple accesses, the method minimizes computational demands, which was especially valuable in the computational constraints of the late when memory costs were high but lookup operations were comparatively inexpensive. Verhoeff emphasized that "when the memories get cheaper, algorithms can be (economically) replaced by table look-up," highlighting the method's practicality for scaling with hardware advancements. The approach also facilitates hardware implementations, such as embedding the tables in () for fast lookups in dedicated circuits, as seen in practical applications like the switching system for the library, where table-based error detection supported automated processing of decimal codes. This hardware suitability stems from the method's reliance on straightforward indexing, avoiding arithmetic beyond basic array retrievals.

Programming Examples

The Verhoeff algorithm's table-driven implementation relies on three key tables: a 10×10 multiplication table derived from the D₅, an 8×10 table for positional weighting, and a 10-element table to determine the . These tables enable efficient computation without direct group operations.

Pseudocode

The following pseudocode outlines the core functions for computing a check digit and validating a code, processing digits from right to left with increasing permutation powers modulo 8.
function compute_checksum(code, includes_check_digit):
    checksum = 0
    n = length(code)
    for i = 0 to n-1:
        idx = n - (i + 1)  // right-to-left index
        num = integer_value(code[idx])
        if num < 0 or num > 9:
            raise InvalidDigitError
        pos = if includes_check_digit then i else i + 1
        checksum = multiplication_table[checksum][permutation_table[pos % 8][num]]
    return checksum

function compute_check_digit(code):
    if code is empty or null:
        raise InvalidInputError
    checksum = compute_checksum(code, false)
    return inverse_table[checksum]

function validate(code):
    if code is empty or null or length(code) < 1:
        return false
    checksum = compute_checksum(code, true)
    return checksum == 0
This pseudocode mirrors the standard loop-based evaluation, where the final checksum must be 0 for valid codes including the check digit.

Python Example

In Python, the tables can be defined as lists of lists, with the implementation handling string inputs of digits. The example below includes the standard tables and functions for computation and validation.
python
# Multiplication table (d)
d = [
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
    [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
    [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
    [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
    [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
    [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
    [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
    [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
    [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
]

# Permutation table (p), 8 rows for mod 8
p = [
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
    [5, 8, 0, 3, 7, 9, 4, 1, 6, 2],
    [8, 0, 4, 6, 9, 1, 2, 5, 3, 7],
    [0, 4, 9, 7, 3, 6, 1, 8, 2, 5],
    [4, 9, 3, 5, 8, 2, 6, 7, 1, 0],
    [9, 3, 6, 8, 2, 5, 7, 0, 4, 1],
    [3, 6, 1, 2, 5, 0, 8, 4, 7, 9]
]

# Inverse table (inv)
inv = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]

def compute_checksum(code, includes_check_digit):
    if not code:
        raise ValueError("Code cannot be empty")
    checksum = 0
    n = len(code)
    for i in range(n):
        idx = n - (i + 1)
        try:
            num = int(code[idx])
        except ValueError:
            raise ValueError(f"Invalid [digit](/page/Digit) at [position](/page/Position) {idx}: '{code[idx]}'")
        if num < 0 or num > 9:
            raise ValueError(f"[Digit](/page/Digit) out of [range](/page/Range) at [position](/page/Position) {idx}: {num}")
        pos = i if includes_check_digit else i + 1
        checksum = d[checksum][p[pos % 8][num]]
    return checksum

def compute_check_digit(code):
    checksum = compute_checksum(code, False)
    return str(inv[checksum])

def validate(code):
    if not code or len(code) < 1:
        return False
    try:
        checksum = compute_checksum(code, True)
        return checksum == 0
    except ValueError:
        return False

# Example usage
print(compute_check_digit("142857"))  # Output: '7' (for 1428577)
print(validate("1428577"))  # True
This Python implementation processes the input string directly, raising exceptions for invalid inputs, and uses list indexing for table lookups.

JavaScript Example

For web-based validation, JavaScript can use arrays for the tables, suitable for browser or Node.js environments. The example includes error handling via thrown errors.
javascript
// Multiplication table (d)
const d = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
  [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
  [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
  [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
  [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
  [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
  [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
  [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
  [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
];

// Permutation table (p)
const p = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
  [5, 8, 0, 3, 7, 9, 4, 1, 6, 2],
  [8, 0, 4, 6, 9, 1, 2, 5, 3, 7],
  [0, 4, 9, 7, 3, 6, 1, 8, 2, 5],
  [4, 9, 3, 5, 8, 2, 6, 7, 1, 0],
  [9, 3, 6, 8, 2, 5, 7, 0, 4, 1],
  [3, 6, 1, 2, 5, 0, 8, 4, 7, 9]
];

// Inverse table
const inv = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9];

function computeChecksum(code, includesCheckDigit) {
  if (!code || code.length === 0) {
    throw new Error('Code cannot be empty');
  }
  let checksum = 0;
  const n = code.length;
  for (let i = 0; i < n; i++) {
    const idx = n - (i + 1);
    const char = code.charAt(idx);
    const num = parseInt(char, 10);
    if (isNaN(num) || num < 0 || num > 9) {
      throw new Error(`Invalid digit at position ${idx}: '${char}'`);
    }
    const pos = includesCheckDigit ? i : i + 1;
    checksum = d[checksum][p[pos % 8][num]];
  }
  return checksum;
}

function computeCheckDigit(code) {
  const checksum = computeChecksum(code, false);
  return inv[checksum].toString();
}

function validate(code) {
  if (!code || code.length < 1) {
    return false;
  }
  try {
    const checksum = computeChecksum(code, true);
    return checksum === 0;
  } catch (e) {
    return false;
  }
}

// Example usage
console.log(computeCheckDigit('142857'));  // '7'
console.log(validate('1428577'));  // true
This version is array-based and throws errors for non-numeric characters, making it suitable for form validation in web applications.

Edge Cases in Code

Implementations must handle edge cases to ensure robustness. For empty or null inputs, both and examples raise errors or return false for validation, preventing on invalid . Non-numeric inputs, such as letters or symbols, trigger exceptions with descriptive messages, as seen in the try-parse logic; for instance, compute_check_digit("12a") would fail with an invalid digit error. There is no inherent maximum length limit due to the modulo-8 indexing, allowing arbitrary-length numeric strings, but very long inputs (e.g., exceeding JavaScript's string limits) may hit runtime constraints in . For single-digit codes, validation checks if the digit is its own inverse under the tables, such as validating "0" as true since the checksum starts and ends at 0.

Applications and Comparisons

Real-World Uses

The Verhoeff algorithm found early adoption in the serial numbering of German banknotes during the 1970s and beyond, providing robust error detection for high-value financial instruments. This application leveraged the algorithm's ability to catch single-digit errors and adjacent transpositions, enhancing integrity in currency handling and accounting systems. In national identification systems, the algorithm is prominently used for India's numbers, a 12-digit for over 1.3 billion residents, where the final digit serves as a to validate against transcription errors during enrollment and . Similarly, Luxembourg's Numéro d'Identification National employs the Verhoeff to compute the 13th from the birth date and sequence components, ensuring accuracy in administrative and processing. The algorithm also underpins the check digit in SNOMED CT identifiers, a global clinical terminology standard used in electronic health records across healthcare systems in multiple countries, where it verifies the integrity of codes during and . In modern contexts, the Verhoeff algorithm persists in legacy financial systems requiring stringent error detection, such as older banking infrastructures, and in specialized software for validating product codes, numbers, and other numeric identifiers where errors are a concern. Despite its strengths, widespread adoption has been limited by the relative complexity compared to simpler methods like the , though it remains valuable in high-integrity applications demanding comprehensive error coverage.

Differences from Other Checksum Algorithms

The Verhoeff algorithm distinguishes itself from the primarily in its superior detection of transposition errors. While the , a modulo-10 weighted commonly used for validation, detects nearly all single-digit errors and about 98% of adjacent transpositions but misses specific cases like the swap of 0 and 9, the Verhoeff algorithm achieves 100% detection for all single-digit errors and all adjacent transpositions through its use of operations. This enhanced reliability comes at the cost of greater complexity, as Luhn's simplicity facilitates its widespread adoption in applications requiring quick manual or automated checks, such as ISO/IEC 7812-compliant payment systems. In comparison to modulo-11 checksums, such as those employed in ISBNs, the Verhoeff algorithm offers comparable 100% detection of single-digit errors but excels in handling non-adjacent errors due to its non-abelian structure, which detects 95.6% of jump (e.g., to cba) versus modulo-11's limitations in unweighted forms that miss most altogether. Weighted modulo-11 variants improve adjacent detection to 100%, yet they still underperform Verhoeff on phonetic and twin errors, where the group-based approach leverages tables for broader coverage without requiring symbol recoding. The , introduced in 2004, shares the Verhoeff algorithm's core strengths in detecting all single-digit errors and adjacent transpositions but employs a different without position-specific permutations, resulting in a more uniform computation process. As the older method from 1969, Verhoeff relies on precomputed tables (D_5 for decimal digits), enabling 97.9% phonetic error detection and 97.8% twin error detection, while Damm prioritizes simplicity in implementation for similar primary error types. Overall, the Verhoeff algorithm's trade-offs include higher complexity—requiring multiple lookup tables—compared to the simplicity of Luhn or modulo-11, yet it provides improved burst detection approximating 98% for twin and short burst scenarios through its algebraic foundation. This makes it preferable for high-integrity applications like serial numbers or identifiers where transcription errors beyond adjacent swaps are prevalent, though its table-driven nature can increase computational overhead in resource-constrained environments.

References

  1. [1]
    Error Detecting Decimal Codes - Jacobus Verhoeff - Google Books
    Bibliographic information ; Publisher, Mathematisch Centrum, 1969 ; Original from, the University of California ; Digitized, Jun 22, 2009 ; Length, 118 pages.Missing: publication | Show results with:publication
  2. [2]
    Feature Column from the AMS - AMS
    Jacobus (Koos) Verhoeff, the Dutch mathematician who was responsible for the elegant system of designing a decimal code check digit ... publication ... 1969.
  3. [3]
    [PDF] Length 3 Check Digit Codes with Grouped Tags and Disjoint ... - arXiv
    Mar 28, 2024 · Abstract—In 1969 J. Verhoeff provided the first examples of a decimal error detecting code using a single check digit to provide.
  4. [4]
    Feature Column from the AMS
    ### Summary of Verhoeff Algorithm
  5. [5]
    [PDF] error detecting decimal codes
    Description of the various error detecting decimal codes 50. 2.4. Results of a test on life errors. 58. 2.5. Conclusions. Chapter 3. Codes based on the cyclic ...
  6. [6]
    [PDF] Investigation 35 - Check Digits - ScholarWorks@GVSU
    The Verhoeff algorithm detects all single digit errors and all transposition errors made by inter- changing two adjacent digits. Also, the Verhoeff algorithm ...Missing: paper | Show results with:paper
  7. [7]
    [PDF] dihedral groups - keith conrad
    Introduction. For n ≥ 3, the dihedral group Dn is defined as the rigid motions1 taking a regular n-gon back to itself, with the operation being composition.Missing: algorithm | Show results with:algorithm
  8. [8]
    [PDF] Check Digit Schemes and Error Detecting Codes - Union University
    What are error detecting codes? Error detecting codes are algorithms that utilize check digits to detect, but not to correct, errors in entering identification.
  9. [9]
    VerhoeffCheckDigit (Apache Commons Validator 1.10.0 API)
    Verhoeff (Dihedral) Check Digit calculation/validation. Check digit calculation for numeric codes using a Dihedral Group of order 10.Missing: steps | Show results with:steps
  10. [10]
    <b>Optimal Check Digit Systems Based on Modular Arithmetic</b>
    The first one is a Verhoeff system based on anti-symmetric mappings in the numbering used in Deutsche Mark banknotes. However, this system has been discontinued ...
  11. [11]
    Developer Section - Unique Identification Authority of India
    AUA application should have a client level validation for Aadhaar number validity ie. should be 12 digits and conform to Verhoeff algorithm. Ensure you have ...
  12. [12]
    National ID Information - SAP Help Portal
    Digit 13 is a check digit calculated with algorithm VERHOEFF for the positions YYYY MM DD NNN. ... For details to the calculation algorithms for both national ID ...
  13. [13]
    Check digit computation | SNOMED International Documents
    These have been analyzed by in a paper by J. Verhoeff ("Error Detecting Decimal Codes", Mathematical Center Tract 29 , The Mathematical Center, Amsterdam, 1969) ...
  14. [14]
    The Math Trick Hidden in Your Credit Card Number
    Aug 12, 2025 · Verhoeff developed an even more comprehensive algorithm that, in addition to detecting all the same typos as Luhn's algorithm, catches 09/90 ...