Prüfziffer, Barcode

Analog zu den Teilbarkeitsregeln existiert für die wohlbekannten Barcodes, zum Beispiel auf Verpackungen, ein Prüfverfahren.

Bezahlt man in einem Geschäft seine Ware, so wird der Preis in aller Regel nicht von Hand eingegeben. Vielmehr wird der Strichcode, der sich auf jedem Artikel befindet, eingescannt. Selbst wenn dies aufgrund technischer Probleme nicht funktioniert, gibt der Kassierer nicht den Preis, sondern die zum Strichcode gehörende Ziffernfolge ein. Wie kommt es, dass ein Fehler beim Eingeben des Barcodes fast immer auffällt? Fallen Fehler wirklich immer auf?

Man braucht also eine Idee, wie man Fehler bei der Eingabe mit hoher Wahrscheinlichkeit bemerken kann – und diese Idee heisst Redundanz. Man hängt an den Teil des Codes, den man zur Identifikation des Produkts braucht, zusätzliche (redundante) Ziffern an, deren einziger Sinn es ist, den Code fehlerresistenter zu machen. Dabei sollten möglichst wenige zusätzliche Ziffern eine möglichst hohe Sicherheit bieten. Weshalb und wie man das mit nur einer Ziffer erreichen kann, hat mit Modulo-Arithmetik zu tun. Ähnliche Verfahren werden auch bei Kreditkarten, ISBN-Nummern, Seriennummern von Geldscheinen etc. verwendet.

Prüfziffer (Checksum)

Als kleines Beispiel betrachten wir das alte ISBN-System (International Standard Book Number). Dieses ist zwar nicht mehr aktuell, aber für unsere Zwecke instruktiver und einfacher verständlich als die inzwischen verwendete Variante. Die hier betrachteten Überlegungen gelten grundsätzlich auch für die meisten neueren Verfahren.

Zur Einordnung: Jedem Buch ist zur eindeutigen Identifikation eine zehnstellige ISBN-Nummer zugeordnet. Von diesen 1010 Ziffern sind die ersten 99 die eigentliche Information; die zehnte ist eine Prüfziffer. Diese Prüfziffer, auch Checksum genannt, soll eine gewisse Sicherheit zur Vermeidung von Tipp- und Übertragungsfehlern gewährleisten.

Example 1

Wir nehmen als Beispiel das Buch

P. Hartmann, Mathematik für Informatiker, Vieweg, 2003. mit der (alten) ISBN-Nummer 3-8348-0096-1.

Dabei steht die erste Ziffer für die Länder- oder Sprachgruppe, die zweite Zifferngruppe für den Verlag und die dritte für die vom Verlag vergebene Titelnummer. Die 10. Ziffer, die 1, ist die Prüfziffer.

Eine ISBN-Nummer sei mit

a1a2a3a9a10=:Ya_1a_2a_3\dots a_9a_{10} =: Y

bezeichnet. Dabei ist ai{0,1,2,,9}a_i\in\{0,1,2,\dots,9\} für i=1,,9i=1,\dots,9 und die Prüfziffer a10a_{10} berechnet sich gemäss:

a10a1+2a2+3a3++9a9i=19iaimod11.a_{10}\equiv a_1+2a_2+3a_3+\dots+9a_9\equiv\sum_{i=1}^9ia_i\mod11.

Dies ergibt für a10a_{10} einen Wert zwischen 00 und 1010. Falls das Ergebnis 1010 lautet, schreibt man dafür römisch X.

Natürlich kann man nicht erwarten, dass so alle Fehler erkannt werden. Aber, wie wir gleich sehen werden, schützt diese Prüfziffer vor den alltäglichsten Fehlern.

Ziffer fehlerhaft eingetippt

Geschieht der Fehler in der Prüfziffer selbst, dann ist der Fall klar. Wir können also den Fehler zwischen den Stellen 11 und 99 annehmen und müssen zeigen, dass die Prüfziffer falsch ist. Sei also

Y=a1aia10Y'=a_1\dots a_i'\dots a_{10}

die fehlerhafte ISBN mit dem Fehler an der ii-ten Stelle, 1i91\leq i\leq9. Die Prüfziffer wäre so

a10=jijaj+iaimod11a'_{10}=\sum_{j\neq i}ja_j+ia_i'\mod11

Wir haben aber a10a_{10} und es gilt

a10a10=iaiiai=i(aiai)mod11a_{10}-a_{10}'=ia_i-ia_i'=i(a_i-a_i')\mod11

Wegen aiaia_i\neq a_i' ist aiai0a_i-a_i'\neq0 und wegen 1i91\leq i\leq9 auch i0i\neq0. Daraus folgt mit einer nicht ganz trivialen Überlegung, dass i(aiai)≢0mod11i(a_i-a_i')\not\equiv0\mod11. Dabei ist die Wahl des Moduls wichtig. Entscheidend für die letzte Folgerung ist, dass 1111 eine Primzahl ist, denn für diese haben wir

Theorem 1: Lemma von Euklid

Sind pp prim und a,bZa,b\in\mathbb{Z} mit pabp\mid ab, so gilt pap\mid a oder pbp\mid b.

Proof

Wir brauchen die Negation des obigen Satzes:

Teilt pp weder aa noch bb, dann teilt pp auch nicht abab.

Wenn pp weder aa noch bb teilt, dann kommt pp nicht in der Primfaktorzerlegung von aa und bb vor, also auch nicht in der Primzahlzerlegung des Produkts abab. Das heisst pp teilt abab nicht.

Damit ist gezeigt, dass insbesondere für p=11p=11 der letzte Schritt gilt und damit die Prüfziffer der ISBN eine fehlerhafte Ziffer erkennt.

Exercise 1: Vertipper

Früher wurde für die Buchklassifikation anstelle der heute gebräuchlichen ISBN13- die ISBN10-Nummer verwendet. Diese beiden Nummern unterscheiden sich nur durch das Präfix 978978 und die Prüfziffer, die restlichen 99 Ziffern stimmen überein. Ich habe mich in diesem Beispiel für die ISBN10 entschieden, da sie rechnerisch etwas handlicher ist.

Das Buch Mathematik für Informatiker hat die ISBN-10 Nummer 3-8348-0096-1, wobei die letzte Ziffer, 1, als Prüfziffer (Checksum) fungiert. Diese letzte Ziffer a10a_{10} wird in der ISBN10-Variante aus den vorangehenden Ziffern wie folgt berechnet:

a10k=19kakmod11.a_{10}\equiv\sum_{k=1}^9k\cdot a_k\mod11.

a) Überprüfe, ob die Checksum 1 korrekt ist.

b) Wähle eine beliebige Position in der ISBN-Nummer aus und vertippe dich (absichtlich). Berechnen nun die Prüfziffer der "vertippten" Nummer.

c) Angenommen, man vertippe sich an genau 22 Stellen, sagen wir Positionen 22 und 55. Registriert die Prüfziffer die Vertipper? Finde ein Beispiel für einen "Doppelvertipper", der nicht erkannt wird.

Solution

a) Es ist 13+28+33+44+58+60+70+89+96=2101mod11.1\cdot3+2\cdot8+3\cdot3+4\cdot4+5\cdot8+6\cdot0+7\cdot0+8\cdot9+9\cdot6=210\equiv1\mod11.

b) Wenn ich mich zum Beispiel an der Stelle 55 vertippe, 38349009613834\textcolor{red}{9}00961, dann kriege ich als Checksum 6≢16\not\equiv1.

c) Wenn man sich an den Stellen 22 und 55 vertippt, dann hat man als Differenz der Checksums

a10a10=2(aiai)+5(ajaj).a_{10}-a'_{10}=2(a_i-a'_i)+5(a_j-a'_j).

Nun probiert man für aia_i, aja_j, aia'_i, aj{0,1,9}a'_j\in\{0,1,\dots9\} alle Kombinationen aus, die mod110\mod11\equiv 0 ergeben. Denn in diesem Fall werden die Vertipper nicht bemerkt. Das ist im Allgemeine aufwändig und von den konkreten Ziffern abhängig. Für die vorliegende Nummer klappt dies beispielsweise für 37344009613\textcolor{red}{7}34\textcolor{red}{4}00961, da 2(87)+5(84)=220mod112\cdot(8-7)+5\cdot(8-4)=22\equiv0\mod11.

Note 1

All die Erkennung der "einfachen" Vertipper würde nicht funktionieren, wenn wir anstelle von 1111 beispielsweise 1010 als Modul verwendet hätten. Denn wegen 10=2510=2\cdot5, also 250mod102\cdot5\equiv0\mod10 würde etwa ein Fehler an der i=5i=5-ten Stelle nicht erkannt, wenn die fehlerhafte Ziffer um 22 von der korrekten Ziffer abweicht. Die Prüfziffer Modulo 1010 bliebe dann unverändert.

Exercise 2: Vertauscher

Schreibe eine Python-Funktion, die zu gegebenem Input einer ISBN10-Nummer als Output angibt, ob die Nummer korrekt ist oder nicht.

Verwende anschliessend das Programm, um zu testen, dass die Prüfziffer bemerkt, falls man zwei Ziffern vertauscht hat.

Solution

Ein Beispiel ist

def isbncheck(isbnnr):
    # Einzelne Ziffern aus der Nummer listen
    isbnls = [int(num) for num in str(isbnnr)]
    summod = 0
    # Ziffern gewichten, zusammenzaehlen und mod 11 rechnen
    for k in range(1,10):
        summod = (summod + (k)*isbnls[k-1]) % 11
    # Checksum vergleichen mit Pruefziffer
    if summod == isbnls[9]:
        return print("ok")
    else:
        return print("bärääh")

Robuster und ausführlicher wäre:

# Akzeptiert einen String, der Bindestriche ignoriert
def is_isbn10_valid(isbn_str):
    # Entfernt mögliche Bindestriche und Leerzeichen
    digits = isbn_str.replace("-", "").replace(" ", "")
    
    if len(digits) != 10:
        return False
        
    # Prüfen, ob alle Zeichen (ausser evtl. 'X' am Ende) Ziffern sind
    if not digits[:9].isdigit():
        return False

    checksum = 0
    for i in range(9):
        checksum += (i + 1) * int(digits[i])
    
    # Prüfziffer validieren (kann 'X' sein)
    last_char = digits[9].upper()
    if last_char == 'X':
        correct_checksum = 10
    elif last_char.isdigit():
        correct_checksum = int(last_char)
    else:
        return False # Ungültiges Zeichen an letzter Stelle

    return (checksum % 11) == correct_checksum

# -- So würde man die Funktion verwenden --
my_isbn = "3-8348-0096-1"
if is_isbn10_valid(my_isbn):
    print(f"Die ISBN '{my_isbn}' ist gültig! ✔️")
else:
    print(f"Die ISBN '{my_isbn}' ist fehlerhaft. ❌")

Zahlenvertauscher

Die ISBN erkennt nicht nur einzelne fehlerhafte Ziffern, sondern auch den am häufigsten auftauchende Fehlertyp: das Vertauschen zweier aufeinanderfolgender Ziffern. In der Tat erkennt die Prüfziffer sogar Vertauschen von nicht unmittelbar aufeinanderfolgenden Ziffern, was zwar weniger vorkommt, aber für den folgenden Beweis ohne Mehraufwand mit einbezogen werden kann.

Exercise 3: Vertauscher testen

Teste einen Zahlendreher anhand von 3-8348-0069-0.

Solution

Brauche dein Python-Programm von oben.

Exercise 4: Beweis Vertauscher

Zeige, dass obige Checksum einen Vertauscher bemerkt. Zeige dies für 22 Ziffern, die nicht zwingend aufeinander folgen.

Solution

Seien i,j{1,2,,9}i,j\in\{1,2,\dots,9\} mit iji\neq j die vertauschten Positionen. Die involvierten Ziffern unterschieden sich natürlich auch, aiaja_i\neq a_j, da sonst der Vertauscher gar nicht bemerkt würde. Betrachten Sie die korrekte, a10a_{10}, versus die inkorrekte, a10a'_{10}, Checksum. Die Differenz ist

a10a10=iai+jaj(iaj+jai)=i(aiaj)+j(ajai)=i(aiaj)j(aiaj)=(ij)(aiaj)≢0mod11\begin{align*} a_{10}-a'_{10}&= i\cdot a_i+j\cdot a_j-(i\cdot a_j+j\cdot a_i)\\ &=i\cdot(a_i-a_j)+j\cdot(a_j-a_i)\\ &=i\cdot(a_i-a_j)-j\cdot(a_i-a_j)\\ &=(i-j)\cdot(a_i-a_j)\\ &\not\equiv0\mod11 \end{align*}

wobei man beim letzten Schritt gleich argumentiert wie im vorangegangenen Beweis: teilt die Primzahl 1111 weder den einen noch den andern Faktor, dann sicher auch nicht das Produkt; also teilt 1111 nicht a10a10a_{10}-a'_{10} und damit unterscheiden sich die Prüfziffern Modulo 1111.

(Zahlendreher kommentiert)

Wie oben gesehen, ist also die Wahl des Moduls entscheidend. Bei 99 informationstragenden Ziffern braucht man also eine Primzahl grösser oder gleich 1010, und 1111 ist dafür die kleinstmögliche Wahl.

Exercise 5: 🧩

Bei der ISBN13 berechnet sich die Prüfziffer a13a_{13} via

a1310(k=112ak3(k+1)mod2mod10).a_{13}\equiv10-\left(\sum_{k=1}^{12}a_k\cdot 3^{(k+1)\mod2}\mod10\right).

Überprüfe die Korrektheit der ISBN13 Prüfziffer.

Hier kann man noch für die oben verwendete ISBN10 die zugehörige ISBN13 Checksum nachrechnen:

Solution

Wir berechnen zuerst die gewichtete Summe:

S=91+73+81+33+01+33+31+03+21+53+81+63=9+21+8+9+0+9+3+0+2+15+8+18=102\begin{align*} S &= 9\cdot1 + 7\cdot3 + 8\cdot1 + 3\cdot3 + 0\cdot1 + 3\cdot3 + 3\cdot1 + 0\cdot3 + 2\cdot1 + 5\cdot3 + 8\cdot1 + 6\cdot3 \\ &= 9 + 21 + 8 + 9 + 0 + 9 + 3 + 0 + 2 + 15 + 8 + 18 \\ &= 102 \end{align*}

Nun berechnen wir den Modulo 10 der Summe: 1022(mod10)102 \equiv 2 \pmod{10}. Die Prüfziffer a13a_{13} ist dann:

a13=(102)(mod10)=8.a_{13} = (10 - 2) \pmod{10} = 8.

Das Ergebnis 8 stimmt mit der Prüfziffer der ISBN überein.