El algoritmo Luhn módulo N es una extensión del algoritmo Luhn (también conocido como algoritmo módulo 10) que permite trabajar con secuencias de valores en cualquier base par . Esto puede ser útil cuando se requiere un dígito de control para validar una cadena de identificación compuesta por letras, una combinación de letras y dígitos o cualquier conjunto arbitrario de N caracteres donde N sea divisible por 2.
Explicación informal
Algoritmo original (Luhn mod 10)
El algoritmo original de Luhn fue diseñado por Hans Peter Luhn y patentado en 1960 (como un dispositivo físico para implementar el algoritmo). [ 1 ] Es un algoritmo de dígito de control diseñado para detectar la mayoría de los errores de entrada comunes, al comparar el cálculo de los dígitos de un número de identificación con el último dígito ("de control"). Un uso particularmente común del algoritmo de Luhn se encuentra en las tarjetas de crédito ; el último dígito de un número de tarjeta de crédito de 16 dígitos es el resultado del algoritmo de Luhn aplicado a los primeros 15 dígitos. Los números de identificación gubernamentales, los números de cuenta bancaria, los números ISBN y muchos otros números de identificación utilizan el algoritmo de Luhn o una pequeña modificación del mismo. [ 2 ]
El algoritmo Luhn original es un caso especial del algoritmo Luhn mod N , donde N es igual a 10. Recibe una cadena de dígitos (omitiendo el último dígito), la invierte, multiplica cada segundo dígito por dos y luego suma todos los resultados. El último dígito de esta suma debe ser igual al último dígito de la cadena. [ 2 ] (Para un ejemplo completo de este cálculo, consulte la descripción del algoritmo Luhn ). El objetivo del algoritmo Luhn es detectar errores comunes de entrada al introducir una cadena de dígitos en un ordenador. Detecta todos los errores de sustitución, donde un dígito se reemplaza accidentalmente por otro, así como la mayoría de los errores de transposición, siendo 90 vs. 09 la única excepción. [ 3 ] Aunque es una función hash , no está diseñada para ser criptográficamente segura ni para detectar errores maliciosos o fraude. [ 2 ]
Extendiendo al módulo N
El algoritmo original de Luhn se denomina algoritmo "módulo 10" porque realiza aritmética modular en un sistema de 10 dígitos. El dígito de control se genera sumando el algoritmo de Luhn y calculando el resultado módulo 10, que equivale al resto de la división por 10, es decir, la cifra de las unidades del número. Esta misma idea básica puede aplicarse a un sistema arbitrario de N caracteres ordenados.
El algoritmo Luhn mod N genera un dígito de control (más precisamente, un carácter de control) dentro del mismo rango de caracteres válidos que la cadena de entrada. Por ejemplo, si el algoritmo se aplica a una cadena de letras minúsculas ( de la a a la z ), el carácter de control también será una letra minúscula. Aparte de esta distinción, se asemeja mucho al algoritmo original.
La idea principal de esta extensión es que el conjunto completo de caracteres de entrada válidos se asigna a una lista de puntos de código (es decir, números enteros secuenciales que comienzan en cero). El algoritmo procesa la cadena de entrada convirtiendo cada carácter a su punto de código asociado y luego realizando los cálculos en módulo N (donde N es el número de caracteres de entrada válidos). Finalmente, el punto de código de verificación resultante se vuelve a asignar para obtener su carácter de verificación correspondiente.
Limitación
El algoritmo Luhn mod N solo funciona cuando N es divisible por 2. Esto se debe a que existe una operación para corregir el valor de una posición después de duplicarlo, la cual no funciona cuando N no es divisible por 2. Para aplicaciones que utilizan el alfabeto latino básico ISO, esto no representa un problema, ya que una cadena de letras con mayúsculas y minúsculas iguales tiene 26 puntos de código. Agregar caracteres decimales añade 10 más, y agregar la otra mayúscula añade 26 más, manteniendo así N divisible por 2 en ambos casos.
Explicación
El segundo paso del algoritmo de Luhn reorganiza el valor duplicado de una posición en la base del dígito original sumando los dígitos individuales del valor duplicado cuando se escribe en base N. Este paso da como resultado números pares si el valor duplicado es menor o igual a N , y números impares si el valor duplicado es mayor que N. Por ejemplo, en aplicaciones decimales donde N es 10, los valores originales entre 0 y 4 dan como resultado números pares y los valores originales entre 5 y 9 dan como resultado números impares, reorganizando efectivamente los valores duplicados entre 0 y 18 en un único resultado distinto entre 0 y 9.
Cuando se utiliza una N que no es divisible por 2, este paso devuelve números pares para valores duplicados mayores que N que no se pueden distinguir de valores duplicados menores o iguales a N.
Resultado
El algoritmo no detectará todos los errores de un solo dígito ni todas las transposiciones de dígitos adyacentes si se utiliza un valor de N que no sea divisible por 2. Dado que estas capacidades de detección son la principal fortaleza del algoritmo, esta limitación lo debilita casi por completo. La variación impar del algoritmo Luhn mod N permite aplicaciones donde N no es divisible por 2, reemplazando el valor duplicado en cada posición con el resto de la división del valor de la posición por N , lo que produce restos impares consistentes con el diseño original del algoritmo.
Asignación de caracteres a puntos de código
Inicialmente, se debe crear una correspondencia entre los caracteres de entrada válidos y los puntos de código. Por ejemplo, supongamos que los caracteres válidos son las letras minúsculas de la a a la f . Por lo tanto, una correspondencia adecuada sería:
Tenga en cuenta que el orden de los caracteres es completamente irrelevante. Este otro mapeo también sería aceptable (aunque posiblemente más engorroso de implementar):
También es posible intercalar letras y dígitos (e incluso posiblemente otros caracteres). Por ejemplo, esta correspondencia sería apropiada para dígitos hexadecimales en minúscula :
Algoritmo en C#
Suponiendo que se definen las siguientes funciones:
/// <summary> /// Puede ser cualquier cadena de caracteres. /// </summary> private const string CodePoints = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ;privado int NumberOfValidInputCharacters () => CodePoints . Length ;private int CodePointFromCharacter ( char character ) => CodePoints . IndexOf ( character );private char CharacterFromCodePoint ( int codePoint ) => CodePoints [ codePoint ];La función para generar un carácter de verificación es:
char GenerateCheckCharacter ( string input ) { int factor = 2 ; int sum = 0 ; int n = NumberOfValidInputCharacters ();// Comenzar desde la derecha y trabajar hacia la izquierda es más fácil ya que // el "factor" inicial siempre será "2". for ( int i = input . Length - 1 ; i >= 0 ; i -- ) { int codePoint = CodePointFromCharacter ( input [ i ]); int addend = factor * codePoint ;// Alterna el "factor" por el que se multiplica cada "codePoint" factor = ( factor == 2 ) ? 1 : 2 ;// Suma los dígitos del "suma" como se expresa en base "n" sumando = IntegerValue ( sumando / n ) + ( sumando % n ); suma += sumando ; }// Calcula el número que debe agregarse a la "suma" // para que sea divisible por "n". int resto = suma % n ; int checkCodePoint = ( n - resto ) % n ;return CharacterFromCodePoint ( checkCodePoint ); }Y la función para validar una cadena (con el carácter de verificación como último carácter) es:
bool ValidateCheckCharacter ( string input ) { int factor = 1 ; int sum = 0 ; int n = NumberOfValidInputCharacters ();// Comenzando desde la derecha, trabaja hacia la izquierda // Ahora, el "factor" inicial siempre será "1" // ya que el último carácter es el carácter de verificación. for ( int i = input . Length - 1 ; i >= 0 ; i -- ) { int codePoint = CodePointFromCharacter ( input [ i ]); int addend = factor * codePoint ;// Alterna el "factor" por el que se multiplica cada "codePoint" factor = ( factor == 2 ) ? 1 : 2 ;// Suma los dígitos del "suma" como se expresa en base "n" sumando = IntegerValue ( sumando / n ) + ( sumando % n ); suma += sumando ; }entero resto = suma % n ;devolver ( resto == 0 ); }Algoritmo en Java
Suponiendo que se definen las siguientes funciones:
int codePointFromCharacter ( char character ) {...}char characterFromCodePoint ( int codePoint ) {...}int numberOfValidInputCharacters () {...}La función para generar un carácter de verificación es:
char generateCheckCharacter ( String input ) { int factor = 2 ; int sum = 0 ; int n = numberOfValidInputCharacters ();// Comenzar desde la derecha y trabajar hacia la izquierda es más fácil ya que // el "factor" inicial siempre será "2". for ( int i = input . length () - 1 ; i >= 0 ; i -- ) { int codePoint = codePointFromCharacter ( input . charAt ( i )); int addend = factor * codePoint ;// Alterna el "factor" por el que se multiplica cada "codePoint" factor = ( factor == 2 ) ? 1 : 2 ;// Suma los dígitos del "suma" como se expresa en base "n" sumando = ( sumando / n ) + ( sumando % n ); suma += sumando ; }// Calcula el número que debe agregarse a la "suma" // para que sea divisible por "n". int resto = suma % n ; int checkCodePoint = ( n - resto ) % n ;return characterFromCodePoint ( checkCodePoint ); }Y la función para validar una cadena (con el carácter de verificación como último carácter) es:
boolean validateCheckCharacter ( String input ) { int factor = 1 ; int sum = 0 ; int n = numberOfValidInputCharacters ();// Comenzando desde la derecha, trabaja hacia la izquierda // Ahora, el "factor" inicial siempre será "1" // ya que el último carácter es el carácter de verificación. for ( int i = input . length () - 1 ; i >= 0 ; i -- ) { int codePoint = codePointFromCharacter ( input . charAt ( i )); int addend = factor * codePoint ;// Alterna el "factor" por el que se multiplica cada "codePoint" factor = ( factor == 2 ) ? 1 : 2 ;// Suma los dígitos del "suma" como se expresa en base "n" sumando = ( sumando / n ) + ( sumando % n ); suma += sumando ; }entero resto = suma % n ;devolver ( resto == 0 ); }Algoritmo en JavaScript
Suponiendo que se definen las siguientes funciones:
const codePoints = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ; //Puede ser cualquier cadena de caracteres permitidosfunción numberOfValidInputCharacters () { return codePoints . length ; }función codePointFromCharacter ( personaje ) { return codePoints . indexOf ( personaje ); }función characterFromCodePoint ( codePoint ) { return codePoints . charAt ( codePoint ); }La función para generar un carácter de verificación es:
función generateCheckCharacter ( input ) { let factor = 2 ; let sum = 0 ; let n = numberOfValidInputCharacters ();// Comenzar desde la derecha y trabajar hacia la izquierda es más fácil ya que // el "factor" inicial siempre será "2". for ( let i = input . length - 1 ; i >= 0 ; i -- ) { let codePoint = codePointFromCharacter ( input . charAt ( i )); let addend = factor * codePoint ;// Alterna el "factor" por el que se multiplica cada "codePoint" factor = ( factor == 2 ) ? 1 : 2 ;// Suma los dígitos del "suma" como se expresa en base "n" sumando = ( Math . floor ( sumando / n )) + ( sumando % n ); suma += sumando ; }// Calcula el número que debe sumarse a la "suma" // para que sea divisible por "n". let remainder = sum % n ; let checkCodePoint = ( n - remainder ) % n ; return characterFromCodePoint ( checkCodePoint ); }Y la función para validar una cadena (con el carácter de verificación como último carácter) es:
función validateCheckCharacter ( entrada ) { let factor = 1 ; let suma = 0 ; let n = númeroDeCaracteresDeEntradaValidos ();// Comenzando desde la derecha, trabaja hacia la izquierda // Ahora, el "factor" inicial siempre será "1" // ya que el último carácter es el carácter de verificación. for ( let i = input . length - 1 ; i >= 0 ; i -- ) { let codePoint = codePointFromCharacter ( input . charAt ( i )); let addend = factor * codePoint ;// Alterna el "factor" por el que se multiplica cada "codePoint" factor = ( factor == 2 ) ? 1 : 2 ;// Suma los dígitos del "sumando" expresados en base "n" sumando = ( Math . floor ( sumando / n )) + ( sumando % n ); suma += sumando ; } let resto = suma % n ; return ( resto == 0 ); }Ejemplo
Generación
Considere el conjunto anterior de caracteres de entrada válidos y la cadena de entrada de ejemplo abcdef . Para generar el carácter de verificación, comience con el último carácter de la cadena y muévase hacia la izquierda duplicando cada segundo punto de código. Los "dígitos" de los puntos de código, tal como se escriben en base 6 (ya que hay 6 caracteres de entrada válidos), deben sumarse:
La suma total de dígitos es 14 (0 + 2 + 2 + 1 + 4 + 5). El número que debe sumarse para obtener el siguiente múltiplo de 6 (en este caso, 18 ) es 4. Este es el punto de código de verificación resultante. El carácter de verificación asociado es e .
Validación
La cadena resultante, abcdefe, puede validarse mediante un procedimiento similar:
La suma total de los dígitos es 18. Como es divisible por 6, el carácter de verificación es válido .
Implementación
La asignación de caracteres a puntos de código y viceversa se puede implementar de diversas maneras. El método más sencillo (similar al algoritmo original de Luhn) consiste en utilizar aritmética de códigos ASCII. Por ejemplo, dado un conjunto de entrada de 0 a 9 , el punto de código se puede calcular restando el código ASCII del '0' al código ASCII del carácter deseado. La operación inversa proporciona la asignación inversa. Se pueden manejar rangos adicionales de caracteres mediante sentencias condicionales.
Los conjuntos no secuenciales se pueden mapear en ambos sentidos mediante una sentencia switch/case codificada . Un enfoque más flexible consiste en utilizar algo similar a un array asociativo . Para que esto funcione, se requiere un par de arrays que proporcionen el mapeo bidireccional.
Otra posibilidad es utilizar una matriz de caracteres donde los índices de la matriz sean los puntos de código asociados a cada carácter. El mapeo de carácter a punto de código se puede realizar mediante una búsqueda lineal o binaria . En este caso, el mapeo inverso se realiza simplemente consultando la matriz.
Debilidad
Esta extensión comparte la misma debilidad que el algoritmo original, a saber, no puede detectar la transposición de la secuencia <primer carácter válido><último carácter válido> a <último carácter válido><primer carácter válido > (o viceversa). Esto equivale a la transposición de 09 a 90 (suponiendo un conjunto de caracteres de entrada válidos del 0 al 9 en orden). Como aspecto positivo, cuanto mayor sea el conjunto de caracteres de entrada válidos, menor será el impacto de la debilidad. [ 4 ]
Véase también
Referencias
- ↑ us expired-lifetime US2950048A , Hans Peter Luhn, "Computadora para verificar números", publicado el 23 de agosto de 1960, emitido el 23 de agosto de 1960
- 1 2 3 "¿Qué es el algoritmo de Luhn y cómo funciona? | Stripe" . stripe.com . Consultado el 4 de diciembre de 2025 .
- ↑ "Algoritmo de Luhn para tarjetas de crédito" . Ambimat Electronics . 19 de mayo de 2023. Consultado el 4 de diciembre de 2025 .
- ↑ Johansson, Rickard (2014). Desarrollo de un código knock-out para fines de producción (tesis de maestría en Diseño Técnico). Lund, Suecia: Universidad de Lund. pp. 25, 26. Recuperado el 7 de noviembre de 2025 .
- aritmética modular
- Algoritmos de suma de verificación
