Apuntes DAM
Volver al inicio

Del pseudocódigo a Java: el algoritmo de Luhn de las tarjetas

Ejercicio de JavaMedioUnos 50 minutos

Traduce a Java el algoritmo de Luhn con el que se comprueba que un número de tarjeta no tiene una errata: recorrer las cifras de derecha a izquierda doblando una sí y otra no, detectar la marca por el prefijo, comprobar la longitud, tapar el número y calcular el dígito de control que faltaría.

  • Del pseudocódigo al código
  • Recorrer una cadena al revés
  • charAt y conversión a número
  • Módulo y dígito de control
  • switch como expresión
  • StringBuilder

Enunciado

Los números de tarjeta, el IMEI de los móviles o algunos números de la Seguridad Social llevan al final un dígito de control calculado con el algoritmo de Luhn. No es para la seguridad: sirve para detectar al momento erratas al teclear (una cifra cambiada o dos cifras vecinas intercambiadas) sin tener que preguntar al banco.

Este es el algoritmo en pseudocódigo:

suma ← 0; doblar ← falso; PARA i DESDE la última cifra HASTA la primera: d ← cifra(i); SI doblar: d ← d × 2; SI d > 9: d ← d − 9; suma ← suma + d; doblar ← NO doblar. El número es válido SI suma MOD 10 = 0.

Qué tiene que hacer el programa

  1. Cada línea es un número de tarjeta, que puede llevar espacios o guiones. Sin ellos debe tener de 12 a 19 cifras; si no: «línea»: formato no válido (de 12 a 19 cifras, con espacios o guiones).
  2. La marca, por el principio: 4 Visa (13, 16 o 19 cifras); 34 o 37 American Express (15); de 51 a 55, o de 2221 a 2720, Mastercard (16); cualquier otra, desconocida (cualquier longitud).
  3. Cada número válido de formato escribe el número tapado (todas las cifras menos las 4 últimas cambiadas por *, en grupos de 4 separados por un espacio, o 4-6-5 en American Express), · y la marca, y después · y uno de estos finales: no válida (una Marca no tiene N cifras), válida o no válida (el dígito de control debería ser D).
  4. El dígito de control que debería tener se calcula aplicando el algoritmo a las cifras sin la última, empezando a doblar por la última de ellas: D = (10 − suma % 10) % 10.
  5. Al final: Válidas: V de T (T cuenta todas las líneas no vacías).

Entrada

Un número de tarjeta por línea, con o sin espacios y guiones.

Ejemplos de ejecución

Tu programa debe escribir exactamente esta salida para estas entradas. Las pruebas del editor incluyen estos ejemplos y otros casos ocultos.

Tarjetas de prueba

Entrada

4111 1111 1111 1111
5500-0000-0000-0004
3782 822463 10005
4111 1111 1111 1112

Salida por consola

**** **** **** 1111 · Visa · válida
**** **** **** 0004 · Mastercard · válida
**** ****** *0005 · American Express · válida
**** **** **** 1112 · Visa · no válida (el dígito de control debería ser 1)
Válidas: 3 de 4

Formatos y longitudes

Entrada

1234
4111 1111 1111 111
6011 0009 9013 9424
2221 0000 0000 0009
abcd efgh ijkl

Salida por consola

«1234»: formato no válido (de 12 a 19 cifras, con espacios o guiones)
**** **** ***1 111 · Visa · no válida (una Visa no tiene 15 cifras)
**** **** **** 9424 · desconocida · válida
**** **** **** 0009 · Mastercard · válida
«abcd efgh ijkl»: formato no válido (de 12 a 19 cifras, con espacios o guiones)
Válidas: 2 de 5

Guía paso a paso

Intenta resolverlo por tu cuenta y abre un paso solo cuando te atasques: cada uno te acerca a la solución sin dártela entera.

1. Limpia el número

Quita espacios y guiones con replace y comprueba el formato con matches("\\d{12,19}").

2. El pseudocódigo, línea a línea

Un for desde length() - 1 hasta 0. charAt(i) - '0' convierte el carácter en su valor numérico. La variable doblar cambia en cada vuelta.

java
for (int i = cifras.length() - 1; i >= 0; i--) {
    int d = cifras.charAt(i) - '0';
    if (doblar) {
        d *= 2;
        if (d > 9) d -= 9;
    }
    suma += d;
    doblar = !doblar;
}
3. El dígito de control

Sin la última cifra, la que hay que doblar primero es la que ahora es la última. Por eso el método recibe si el número incluye ya el control (empieza sin doblar) o no (empieza doblando).

4. Tapar el número

"*".repeat(n - 4) más las cuatro últimas, y después ve cortando grupos con un StringBuilder.

Resuélvelo aquí

El editor trae el esqueleto del programa. Pulsa «Ejecutar» para comprobarlo con los ejemplos y con 2 casos ocultos que buscan los errores típicos.

☕JavaDel pseudocódigo a Java: el algoritmo de Luhn de las tarjetasMedio

Ejemplo

Entrada (lo que se escribe por teclado)
4111 1111 1111 1111
5500-0000-0000-0004
3782 822463 10005
4111 1111 1111 1112
Salida esperada
**** **** **** 1111 · Visa · válida
**** **** **** 0004 · Mastercard · válida
**** ****** *0005 · American Express · válida
**** **** **** 1112 · Visa · no válida (el dígito de control debería ser 1)
Válidas: 3 de 4
⏳
Test oculto #3
⏳
Test oculto #4
0/4 tests pasados · pulsa un test para ver su entrada y su salida esperada

Solución explicada

Ver la solución completa
java
1import java.util.Scanner;
2
3public class Main {
4
5    /** Suma de Luhn: desde la derecha, se dobla una cifra sí y otra no (empezando por la segunda si conControl). */
6    static int sumaLuhn(String cifras, boolean conControl) {
7        int suma = 0;
8        boolean doblar = !conControl;
9        for (int i = cifras.length() - 1; i >= 0; i--) {
10            int d = cifras.charAt(i) - '0';
11            if (doblar) {
12                d *= 2;
13                if (d > 9) d -= 9;
14            }
15            suma += d;
16            doblar = !doblar;
17        }
18        return suma;
19    }
20
21    static String marca(String c) {
22        int dos = Integer.parseInt(c.substring(0, 2));
23        int cuatro = Integer.parseInt(c.substring(0, 4));
24        if (c.startsWith("4")) return "Visa";
25        if (dos == 34 || dos == 37) return "American Express";
26        if ((dos >= 51 && dos <= 55) || (cuatro >= 2221 && cuatro <= 2720)) return "Mastercard";
27        return "desconocida";
28    }
29
30    static boolean longitudValida(String marca, int n) {
31        return switch (marca) {
32            case "Visa" -> n == 13 || n == 16 || n == 19;
33            case "American Express" -> n == 15;
34            case "Mastercard" -> n == 16;
35            default -> true;
36        };
37    }
38
39    /** Todas las cifras tapadas menos las 4 últimas, en grupos de 4 (American Express: 4-6-5). */
40    static String tapar(String c, String marca) {
41        String tapado = "*".repeat(c.length() - 4) + c.substring(c.length() - 4);
42        StringBuilder sb = new StringBuilder();
43        int[] grupos = marca.equals("American Express") ? new int[] {4, 6, 5} : null;
44        int i = 0, g = 0;
45        while (i < tapado.length()) {
46            int tam = grupos != null && g < grupos.length ? grupos[g] : 4;
47            if (sb.length() > 0) sb.append(' ');
48            sb.append(tapado, i, Math.min(tapado.length(), i + tam));
49            i += tam;
50            g++;
51        }
52        return sb.toString();
53    }
54
55    public static void main(String[] args) {
56        Scanner sc = new Scanner(System.in);
57        int total = 0, validas = 0;
58        while (sc.hasNextLine()) {
59            String linea = sc.nextLine().trim();
60            if (linea.isEmpty()) continue;
61            total++;
62            String c = linea.replace(" ", "").replace("-", "");
63            if (!c.matches("\\d{12,19}")) {
64                System.out.println("«" + linea + "»: formato no válido (de 12 a 19 cifras, con espacios o guiones)");
65                continue;
66            }
67            String m = marca(c);
68            String inicio = tapar(c, m) + " · " + m;
69            if (!longitudValida(m, c.length())) {
70                System.out.println(inicio + " · no válida (una " + m + " no tiene " + c.length() + " cifras)");
71                continue;
72            }
73            if (sumaLuhn(c, true) % 10 == 0) {
74                validas++;
75                System.out.println(inicio + " · válida");
76            } else {
77                int control = (10 - sumaLuhn(c.substring(0, c.length() - 1), false) % 10) % 10;
78                System.out.println(inicio + " · no válida (el dígito de control debería ser " + control + ")");
79            }
80        }
81        System.out.println("Válidas: " + validas + " de " + total);
82    }
83}

Doblar una cifra sí y otra no hace que intercambiar dos cifras vecinas cambie la suma (casi siempre), y restar 9 cuando el doble pasa de 9 es lo mismo que sumar sus dos dígitos (16 → 1 + 6 = 7 = 16 − 9).

Luhn no protege de nada: cualquiera puede calcular un número válido. Solo evita mandar al banco números con erratas.

El paso del pseudocódigo al código es casi literal: cada línea del algoritmo es una línea de Java. Lo único que añade el lenguaje es cómo se obtiene el valor de un carácter (charAt(i) - '0').

switch como expresión (con -> y yield implícito) devuelve un valor directamente: es ideal para tablas de reglas como la longitud de cada marca.

Para ir más allá

  • Valida también un IMEI (15 cifras, mismo algoritmo).
  • Genera números de prueba válidos para una marca, calculando el dígito de control.
  • Detecta qué errata se cometió: prueba a cambiar cada cifra y di cuáles darían un número válido.

Dónde se explica