La Formula di Luhn
La formula di Luhn, anche conosciuta come Modulo 10, è un semplice
algoritmo che consente di generare e verificare la validità di vari
numeri identificativi. Venne ideata nel 1954 dall’informatico dell’IBM
Hans Peter Luhn e brevettata nel 1960. Ora di pubblico dominio ha
molteplici applicazioni, ad esempio per i numeri delle carte di credito.
Per esempio ogni carta di credito ha un suo numero di carta (es.
0000-1234-5678-9123) dove la prima parte identifica il
circuito internazionale (Visa, American Express, Mastercard…) mentre il
resto la banca emittente ed il cliente.
Descrizione dell’algoritmo per il calcolo della cifra di Luhn
La cifra di controllo di tipo Luhn viene calcolata in modo semplice. Si sommano tutte le cifre in posizione pari al doppio della somma di quelle in posizione dispari. Si considera quindi il modulo rispetto a 10 (ovvero il resto della divisione per 10) del valore così ottenuto; si determina quindi la cifra di Luhn come segue:
- se il modulo è 0 (la somma è divisibile per 10) la cifra di controllo sarà 0. Ad esempio se la somma = 60 che diviso per 10 dà resto 0, la cifra di Luhn sarà 0
- altrimenti la cifra di Luhn sarà la differenza tra 10 ed il modulo. Ad esempio se la somma = 61 che diviso per 10 dà resto 1, la cifra di Luhn sarà 9 (10-1)
Verifica della validità
Il controllo di un numero contenente la cifra di Luhn si basa su tre passi:
- Partendo da destra e spostandosi verso sinistra, moltiplicare per 2 ogni cifra posta in posizione pari
- Laddove la moltiplicazione ha dato un risultato a due cifre, sommare le due cifre per ottenerne una sola (es. 18 = 1+8)
- Sommare tutte le cifre, sia quelle che si trovano in posizione pari, sia quelle che si trovano in posizione dispari
e la somma complessiva è divisibile per 10 (la divisione non ha resto) la carta è valida.
Ad esempio, supponendo di avere il seguente numero di carta: 4716-4359-1733-0099 (quindi 9900367291386278)
- 9+9+0+0+3+6+7+2+9+1+3+8+6+2+7+8=80
- 80/10 = 8 = risultato intero → carta valida
La formula di Luhn viene utilizzata in Canada dal Social Insurance Number per l’identificazione dei suoi clienti; tuttavia con la seguente formula non verifica ulteriori informazioni, come il numero delle cifre e la validità della data di scadenza. Del resto la formula è stata studiata per rilevare errori di digitazione, non è adatta a rilevare falsificazioni volontarie.
# verifica se il numero in input è valido secondo il criterio di Luhn.
# Input: "x" è una stringa numerica
# Output: un vettore contenente:
# - come primo elemento: 1 se il numero è valido altrimenti 0.
# - come secondo elemento: la cifra di luhn.
verificaNumeroLuhn <- function(x, quiet = FALSE){
# Controllo: solo character
if(!inherits(x, "character")) {
stop('x must be a character.')
}
# Tranformo in vettore numerico
y <- strsplit(x, "")[[1]]
y <- as.integer(y)
# Inverto l'ordine
y_reverse <- y[c(seq(length(y),1,-1))]
# Ciclo per il controllo
for(i in 1:length(y_reverse)) {
if(i %% 2 == 0){
# indice pari
y_reverse[i] <- 2*y_reverse[i]
# somma maggiore di 10
if(y_reverse[i] >= 10){
y_split <- as.character(y_reverse[i])
y_split <- strsplit(y_split, "")[[1]]
y_split <- as.integer(y_split)
y_reverse[i] <- sum(y_split)
}
} else {
# indice dispari
next
}
}
y_sum <- sum(y_reverse)
cifra_luhn <- ifelse(y_sum %% 10 == 0, 0, 10 - y_sum %% 10 )
# Controllo Finale Validita
if(cifra_luhn == 0){
if(!quiet) message("Numero Valido")
return(c(validità = 1, cifra_luhn = cifra_luhn))
} else {
if(!quiet) message("Numero NON Valido")
return(c(validità = 0, cifra_luhn = cifra_luhn))
}
}# verifica se il numero in input è valido secondo il criterio di Luhn.
# Input: "x" è una stringa numerica
# Output: un vettore contenente:
# - come primo elemento: 1 se il numero è valido altrimenti 0.
# - come secondo elemento: la cifra di luhn.
verificaNumeroLuhn <- function(x, quiet = FALSE){
# Controllo: solo character
if(!inherits(x, "character")) {
stop('x must be a character.')
}
# Tranformo in vettore numerico
y <- strsplit(x, "")[[1]]
# aggiunge uno 0 se il numero di cifre è dispari
if(length(y) %% 2 != 0){
y[length(y)+1] <- "0"
}
y <- as.integer(y)
# Inverto l'ordine
y_reverse <- y[c(seq(length(y),1,-1))]
# Ciclo per il controllo
for(i in 1:length(y_reverse)) {
if(i %% 2 == 0){
# le cifre in posizione pari vengono raddoppiate
y_reverse[i] <- 2*y_reverse[i]
# SE somma maggiore di 10
if(y_reverse[i] >= 10){
y_split <- as.character(y_reverse[i])
y_split <- strsplit(y_split, "")[[1]]
y_split <- as.integer(y_split)
y_reverse[i] <- sum(y_split)
}
} else {
# SE indice dispari
next
}
}
# Somma totale
y_sum <- sum(y_reverse)
# Calcolo cifra di Luhn
cifra_luhn <- ifelse(y_sum %% 10 == 0, 0, 10 - y_sum %% 10 )
# Controllo Finale Validita
if(cifra_luhn == 0){
if(!quiet) message("Numero Valido")
return(c(validità = 1, cifra_luhn = cifra_luhn))
} else {
if(!quiet) message("Numero NON Valido")
return(c(validità = 0, cifra_luhn = cifra_luhn))
}
}
# Numero carta di esempio
# x <- "4716435917330099"
x = "1131710376"
verificaNumeroLuhn(x, quiet = FALSE)## Numero Valido
## validità cifra_luhn
## 1 0
Verifica di Partite IVA italiane
Per prima cosa dobbiamo capire la struttura di una partita IVA
italiana: per esempio la consideriamo la partita iva
12345678901.
1234567: le prime sette cifre rappresentano il numero di matricola del soggetto assegnato dal relativo ufficio provinciale, che si ottiene incrementando di una unità il numero assegnato al soggetto che lo precede.le cifre dalla ottava alla decima indicano il codice dell’ufficio provinciale del fisco che ha rilasciato la matricola, generalmente corrispondente al codice ISTAT della provincia; l’undicesima cifra, infine, rappresenta un codice di controllo, introdotto al fine di verificare la correttezza delle prime dieci cifre.
L’algoritmo impiegato per calcolare la cifra di controllo è la formula di Luhn.
verificaPartitaIva <- function(x){
# Controllo: solo stringhe
if(!inherits(x, "character") ) {
stop('x must be a character containing a "partita iva".')
}
controllo_luhn <- verificaNumeroLuhn(x, quiet = TRUE)
y = strsplit(x, "")[[1]]
partita_iva <- dplyr::tibble(
pIva = x,
Matricola = paste0(y[1:7], collapse = ""),
Geo = paste0(y[8:10], collapse = ""),
numeroLuhn = y[11],
isValid = controllo_luhn[1] == 1,
)
partita_iva
}Ricerca dell’Ufficio di Emissione
Come seconda cosa possiamo salvare i codici identificativi per ciascuna provincia italiana dal sito dell’ Agenzia delle Entrate ed utilizzarli per identificare il codice geografico di una partita Iva.
library(rvest)
library(stringr)
library(dplyr)
codice_provincia <- function(geo = NULL) {
url <- "https://www1.agenziaentrate.gov.it/documentazione/versamenti/codici/ricerca/VisualizzaTabella.php?settore=V&ArcName=UFFICI"
html <- rvest::read_html(url)
dataset <- rvest::html_nodes(html, css = "table")
dataset <- rvest::html_table(dataset)[[1]]
dataset <- dplyr::mutate(dataset,
Codice = as.character(Codice),
Codice = dplyr::case_when(
stringr::str_length(Codice) == 1 ~ paste0("00", Codice),
stringr::str_length(Codice) == 2 ~ paste0("0", Codice),
stringr::str_length(Codice) == 3 ~ Codice
)
)
dataset <- dplyr::select(dataset, Codice, Provincia = "Descrizione")
# La provincia di bologna è mancante nel sito dell'Agenzia
dataset <- dplyr::bind_rows(dataset,
dplyr::tibble(Codice = "037",
Provincia = "BOLOGNA")
)
if(is.null(geo)){
dataset
} else {
dplyr::filter(dataset, Codice == geo)$Provincia
}
}
codice_provincia()| Codice | Provincia |
|---|---|
| 084 | AGRIGENTO |
| 006 | ALESSANDRIA |
| 042 | ANCONA |
| 007 | AOSTA |
| 051 | AREZZO |
| 044 | ASCOLI PICENO |
| 005 | ASTI |
| 064 | AVELLINO |
| 072 | BARI |
| 025 | BELLUNO |
| 062 | BENEVENTO |
| 016 | BERGAMO |
| 021 | BOLZANO |
| 017 | BRESCIA I |
| 098 | BRESCIA II |
| 074 | BRINDISI |
| 092 | CAGLIARI |
| 085 | CALTANISSETTA |
| 070 | CAMPOBASSO |
| 061 | CASERTA |
| 087 | CATANIA |
| 079 | CATANZARO |
| 069 | CHIETI |
| 013 | COMO |
| 078 | COSENZA |
| 019 | CREMONA |
| 004 | CUNEO |
| 086 | ENNA |
| 038 | FERRARA |
| 048 | FIRENZE I |
| 097 | FIRENZE II |
| 071 | FOGGIA |
| 040 | FORLI’ |
| 060 | FROSINONE |
| 010 | GENOVA I |
| 099 | GENOVA II |
| 031 | GORIZIA |
| 053 | GROSSETO |
| 008 | IMPERIA |
| 094 | ISERNIA |
| 011 | LA SPEZIA |
| 059 | LATINA |
| 075 | LECCE |
| 049 | LIVORNO |
| 046 | LUCCA |
| 043 | MACERATA |
| 020 | MANTOVA |
| 045 | MASSA CARRARA |
| 077 | MATERA |
| 083 | MESSINA |
| 015 | MILANO I |
| 096 | MILANO II |
| 036 | MODENA |
| 063 | NAPOLI I |
| 121 | NAPOLI II |
| 003 | NOVARA |
| 091 | NUORO |
| 028 | PADOVA |
| 082 | PALERMO |
| 034 | PARMA |
| 018 | PAVIA |
| 054 | PERUGIA |
| 041 | PESARO |
| 068 | PESCARA |
| 033 | PIACENZA |
| 050 | PISA |
| 047 | PISTOIA |
| 093 | PORDENONE |
| 076 | POTENZA |
| 088 | RAGUSA |
| 039 | RAVENNA |
| 080 | REGGIO CALABRIA |
| 035 | REGGIO EMILIA |
| 058 | ROMA I |
| 100 | ROMA II |
| 029 | ROVIGO |
| 065 | SALERNO |
| 090 | SASSARI |
| 009 | SAVONA |
| 052 | SIENA |
| 089 | SIRACUSA |
| 014 | SONDRIO |
| 073 | TARANTO |
| 067 | TERAMO |
| 055 | TERNI |
| 001 | TORINO |
| 081 | TRAPANI |
| 022 | TRENTO |
| 026 | TREVISO |
| 032 | TRIESTE |
| 030 | UDINE |
| 012 | VARESE |
| 027 | VENEZIA |
| 002 | VERCELLI |
| 023 | VERONA |
| 024 | VICENZA |
| 056 | VITERBO |
| 037 | BOLOGNA |
Esempio 1
# Partita Iva Università di Bologna
x <- "01131710376"
y <- verificaPartitaIva(x)
y| pIva | Matricola | Geo | numeroLuhn | isValid |
|---|---|---|---|---|
| 01131710376 | 0113171 | 037 | 6 | FALSE |
x <- "0113171037"
verificaNumeroLuhn(x)## Numero Valido
## validità cifra_luhn
## 1 0
codice_provincia(y$Geo)## [1] "BOLOGNA"