Luhn mod N algorithm

From Wikipedia, the free encyclopedia

The Luhn mod N algorithm is an extension to the Luhn algorithm (also known as mod 10 algorithm) that allows it to work with sequences of non-numeric characters. This can be useful when a check digit is required to validate an identification string composed of letters, a combination of letters and digits or even any arbitrary set of characters.

Contents

The Luhn mod N algorithm generates a check digit (more precisely, a check character) within the same range of valid characters as the input string. For example, if the algorithm is applied to a string of lower-case letters (a to z), the check character will also be a lower-case letter. Apart from this distinction, it resembles very closely the original algorithm.

The main idea behind the extension is that the full set of valid input characters is mapped to a list of code-points (i.e., sequential integers beginning with zero). The algorithm processes the input string by converting each character to its associated code-point and then performing the computations in mod N (where N is the number of valid input characters). Finally, the resulting check code-point is mapped back to obtain its corresponding check character.

Initially, a mapping between valid input characters and code-points must be created. For example, consider that the valid characters are the lower-case letters from a to f. Therefore, a suitable mapping would be:

Character a b c d e f
Code-point 0 1 2 3 4 5

Note that the order of the characters is completely irrelevant. This other mapping would also be acceptable (although possibly more cumbersome to implement):

Character c e a f b d
Code-point 0 1 2 3 4 5

It is also possible to intermix letters and digits (and possibly even other characters). For example, this mapping would be appropriate for lower-case hexadecimal digits:

Character 0 1 2 3 4 5 6 7 8 9 a b c d e f
Code-point 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Assuming the following functions are defined:

function int CodePointFromCharacter(char character) {...}

function char CharacterFromCodePoint(int codePoint) {...}

function int NumberOfValidInputCharacters() {...}

The function to generate a check character is:

function char GenerateCheckCharacter(string input) {

        int factor = 2;
        int sum = 0;
        int n = NumberOfValidInputCharacters();

        // Starting from the right and working leftwards is easier since 
        // the initial "factor" will always be "2" 
        for (int i = input.Length - 1; i >= 0; i--) {
                int codePoint = CodePointFromCharacter(input[i]);
                int addend = factor * codePoint;

                // Alternate the "factor" that each "codePoint" is multiplied by
                factor = (factor == 2) ? 1 : 2;

                // Sum the digits of the "addend" as expressed in base "n"
                addend = (addend / n) + (addend % n);
                sum += addend;
        }

        // Calculate the number that must be added to the "sum" 
        // to make it divisible by "n"
        int remainder = sum % n;
        int checkCodePoint = n - remainder;
        checkCodePoint %= n;

        return CharacterFromCodePoint(checkCodePoint);
}

And the function to validate a string (with the check character as the last character) is:

function bool ValidateCheckCharacter(string input) {

        int factor = 1;
        int sum = 0;
        int n = NumberOfValidInputCharacters();

        // Starting from the right, work leftwards
        // Now, the initial "factor" will always be "1" 
        // since the last character is the check character
        for (int i = input.Length - 1; i >= 0; i--) {
                int codePoint = CodePointFromCharacter(input[i]);
                int addend = factor * codePoint;

                // Alternate the "factor" that each "codePoint" is multiplied by
                factor = (factor == 2) ? 1 : 2;

                // Sum the digits of the "addend" as expressed in base "n"
                addend = (addend / n) + (addend % n);
                sum += addend;
        }

        int remainder = sum % n;

        return (remainder == 0);
}

Consider the above set of valid input characters and the example input string abcdef. To generate the check character, start with the last character in the string and move left doubling every other code-point. The "digits" of the code-points as written in base 6 (since there are 6 valid input characters) should then be summed up:

Character a b c d e f
Code-point 0 1 2 3 4 5
Double 2 6 (base 10)
10 (base 6)
10 (base 10)
14 (base 6)
Reduce 0 2 2 1 + 0 4 1 + 4
Sum of digits 0 2 2 1 4 5

The total sum of digits is 14 (0 + 2 + 2 + 1 + 4 + 5). The number that must be added to obtain the next multiple of 6 (in this case, 18) is 4. This is the resulting check code-point. The associated check character is e.

The resulting string abcdefe can then be validated by using a similar procedure:

Character a b c d e f e
Code-point 0 1 2 3 4 5 4
Double 2 6 (base 10)
10 (base 6)
10 (base 10)
14 (base 6)
Reduce 0 2 2 1 + 0 4 1 + 4 4
Sum of digits 0 2 2 1 4 5 4

The total sum of digits is 18. Since it is divisible by 6, the check character is valid.

The mapping of characters to code-points and back can be implemented in a number of ways. The simplest approach (akin to the original Luhn algorithm) is to use ASCII code arithmetic. For example, given an input set of 0 to 9, the code-point can be calculated by subtracting the ASCII code for '0' from the ASCII code of the desired character. The reverse operation will provide the reverse mapping. Additional ranges of characters can be dealt with by using conditional statements.

Non-sequential sets can be mapped both ways using a hard-coded switch/case statement. A more flexible approach is to use something similar to a Java Map or a Hashtable. For this to work, a pair is required to provide the two-way mapping.

An additional possibility is to use an array of characters where the array indexes are the code-points associated with each character. The mapping from character to code-point can then be performed with a linear or binary search. In this case, the reverse mapping is just a simple array lookup.

This extension shares the same weakness as the original algorithm, namely, that it can't detect the transposition of the sequence to (or vice-versa). This is equivalent to the transposition of 09 to 90 (assuming a set of valid input characters from 0 to 9 in order). On a positive note, the larger the set of valid input characters, the smaller the impact of the weakness.

Implementation in Java, JavaScript at modp.com

Advanced Search
Included Web Search Engines


Safe Search

close

Top Matching Results

Occasionally Search.com will highlight specialized results that are based on the context of your query. Examples of specialized results include specific links to news, images, or video.

Top Matching Results may highlight information from other Search.com pages, content from the CNET Network of sites, or third party content. The listings are based purely on relevance. Search.com does not receive payment for listings in this section but our partners that provide this data may get paid for listing these products.

Sponsored Links

This section contains paid listings which have been purchased by companies that want to have their sites appear for specific search terms and related content. These listings are administered, sorted and maintained by a third party and are not endorsed by Search.com.

Search Results

Search.com sends your search query to several search engines at one time and integrates the results into one list which has been sorted by relevance using Search.com's proprietary algorithm. You can customize the list of search engines included in your metasearch from the preferences.

The search engines that are used in your metasearch may allow companies to pay to have their Web sites included within the results. To view the Paid Inclusion policy for a specific search engine, please visit their Web site. Search.com does not accept payment or share revenue with any search engine partner for listings in this section.