Wiskunde & validatie · 12 min lezen
De elfproef ontleed — waarom BSN gewichten 9-8-7-6-5-4-3-2-(-1) gebruikt
Door Alex · Gepubliceerd 17 april 2026
De elfproef voor het Nederlandse Burgerservicenummer wordt vaak als gegeven gepresenteerd: "gewichten 9-8-7-6-5-4-3-2-(-1), modulo 11, check op nul". Dat is correct, maar verklaart niet waarom deze reeks is gekozen — en waarom ze wezenlijk anders werkt dan Luhn (creditcards) of Verhoeff (Indiase Aadhaar). Dit artikel ontleedt het algoritme wiskundig, vergelijkt het met alternatieven op basis van foutdetectie-eigenschappen en sluit af met een werkende TypeScript-referentie plus benchmarkcijfers.
1. Wat de elfproef doet in één regel
Een BSN bestaat uit 9 cijfers d₁ d₂ d₃ d₄ d₅ d₆ d₇ d₈ d₉. De elfproef stelt dat:
(9·d₁ + 8·d₂ + 7·d₃ + 6·d₄ + 5·d₅ + 4·d₆ + 3·d₇ + 2·d₈ − 1·d₉) mod 11 = 0
Drie dingen vallen op: de gewichten zijn aflopend, ze beginnen bij 9 (niet bij een willekeurig getal), en het laatste gewicht is negatief. Dat laatste is geen stijlkeuze — het is de kern van waarom het algoritme werkt.
2. Waarom modulo 11?
Modulo 11 wordt gekozen om één reden: 11 is priem. In een priemveld ℤ/pℤ heeft elk element een unieke inverse, waardoor vermenigvuldiging injectief is voor elk niet-nul gewicht. Dat heeft een direct gevolg voor foutdetectie: als je één cijfer wijzigt, verandert de checksum gegarandeerd — omdat het verschil (nieuwe − oude waarde) × gewicht nooit 0 modulo 11 wordt, behalve bij gelijke waarden.
Modulo 10 (zoals Luhn gebruikt) voldoet hier niet aan, omdat 10 = 2·5 samengesteld is. Daardoor kan Luhn single-digit-fouten missen bij specifieke cijferparen (9↔0 bij posities waar gewicht 1 is).
In cijfers:
- Elfproef vangt 100 % van alle single-digit-fouten op één cijfer.
- Elfproef vangt 100 % van alle aangrenzende transposities (di ↔ di+1).
- Luhn (mod 10) vangt single-digit-fouten volledig op, maar mist transposities 09↔90 en 90↔09.
3. Waarom juist gewicht −1 op positie 9?
De gewichten 9, 8, 7, …, 2 voor de eerste acht cijfers lijken willekeurig gekozen maar vormen in feite een lineair aflopende reeks. De truc zit in de laatste positie, waar gewicht −1 (soms geschreven als +10) staat in plaats van +1. Dit is exact wat de elfproef onderscheidt van een gewone weighted-sum checksum.
Met gewicht +1 zou de som altijd in {0,1,…,10} liggen en zou een BSN waarvan de som gelijk is aan 10 geen geldige controle hebben zonder een speciale representatie (X in ISBN-10, bijvoorbeeld). Door positie 9 met negatief gewicht te verrekenen, kan het cijfer op die positie elke waarde 0–9 aannemen en de elfproef heeft altijd precies één geldige waarde — zolang de som van de andere acht cijfers mod 11 niet 10 is.
Dat laatste is de reden waarom er 9-cijferige combinaties zijn die geen geldig BSN kunnen vormen, ongeacht welk laatste cijfer je kiest. Concreet: als de partiële som 9·d₁ + 8·d₂ + … + 2·d₈ modulo 11 gelijk is aan 10, moet d₉ gelijk zijn aan 10 — onmogelijk met een enkel decimaal cijfer. Daardoor is precies 1 op de 11 willekeurige 9-cijferige prefixen structureel ongeldig.
4. Elfproef vs. Luhn vs. Verhoeff
Voor wie de elfproef afzet tegen bekende alternatieven, een eerlijke vergelijking op drie criteria:
| Eigenschap | Elfproef (BSN) | Luhn (creditcards) | Verhoeff (Aadhaar) |
|---|---|---|---|
| Single-digit detectie | 100 % | 100 % | 100 % |
| Aangrenzende transposities | 100 % | ~97,8 % | 100 % |
| Phonetic errors (bv. "dertig" ↔ "13") | 0 % | 0 % | ~86 % |
| Implementatiecomplexiteit | Laag (1 loop) | Gemiddeld (dubbelen, alterneren) | Hoog (3 lookup tables) |
| Representeerbaarheid | 91 % (1 op 11 ongeldig) | 100 % | 100 % |
De elfproef wint op foutdetectie tegenover Luhn, maar betaalt daarvoor met een representatietekort (9 % van alle 9-cijferige getallen kan geen BSN zijn). Verhoeff behoudt 100 % representatie en voegt phonetic-error-detectie toe, ten koste van aanzienlijk meer complexiteit. Voor een nationaal identificatienummer dat handmatig ingetypt wordt op papieren formulieren was de elfproef in 1988 — toen het sofinummer werd ingevoerd — een pragmatische keuze: genoeg foutdetectie, minimale rekenlast voor mainframes.
5. Referentie-implementatie (TypeScript)
De kortste correcte implementatie past in 10 regels. Hieronder de versie die ook door de BSN-validator op test-nummers.nl gebruikt wordt:
export function isValidBsn(input: string): boolean {
if (!/^\d{8,9}$/.test(input)) return false;
const bsn = input.padStart(9, '0');
const weights = [9, 8, 7, 6, 5, 4, 3, 2, -1];
const sum = [...bsn].reduce(
(acc, char, i) => acc + Number(char) * weights[i],
0,
);
// Strikte regel: BSN met alleen nullen is nooit geldig
if (bsn === '000000000') return false;
return sum % 11 === 0;
}Twee subtiele details zijn belangrijk:
- 8- of 9-cijferige invoer. Historisch werden BSN-nummers tot 10 miljoen soms geprint met 8 cijfers (voorloopnul weggelaten). Goede validators accepteren beide en padden links met nullen. De BRP-koppelingen van de Belastingdienst en DigiD werken intern altijd met 9 cijfers.
- Alleen-nullen. 000000000 doorstaat de elfproef wiskundig (sum = 0), maar is per conventie geen geldig BSN — daarom de expliciete check.
6. Benchmark — hoe snel is het?
Op een MacBook Air M2 (Node 22.9, V8 12.4) haalt bovenstaande referentie-implementatie:
| Scenario | ops/sec | µs per call |
|---|---|---|
| Reduce-based (zie hierboven) | ~6,2 M | ~0,16 |
| For-loop zonder reduce (geoptimaliseerd) | ~14,8 M | ~0,068 |
| Batch van 10.000 (vectorized, Uint8Array) | ~72,5 M (per BSN) | ~0,014 |
Conclusie: BSN-validatie is zo goedkoop dat het nooit een bottleneck hoeft te zijn. Zelfs met een reduce-variant kun je meer dan 6 miljoen validaties per seconde doen op één core. Voor batchgeneratie (zoals onze dataset-generator) is een Uint8Array-loop wel ~4× sneller.
7. Veelgemaakte implementatiefouten
- Gewichten in verkeerde volgorde. Sommige tutorials schrijven gewichten als 2-3-4-5-6-7-8-9-(-1), wat het algoritme omkeert. De officiële volgorde is aflopend: 9 op positie 1, 2 op positie 8, −1 op positie 9.
- Negatieve modulo in talen zonder Euclidische semantiek. In C en Java geeft
-3 % 11als resultaat −3, niet 8. Voor de elfproef werkt de som altijd toe naar 0 mod 11, dus dit is vaak onzichtbaar — maar bij het genereren van het controlecijfer moet je expliciet((x % 11) + 11) % 11schrijven. - Formatting overslaan.Invoer "123.456.789" of "123 456 789" is semantisch geldig maar moet genormaliseerd worden voordat de regex-check plaatsvindt. Niet-genormaliseerde invoer verwerpt geldige BSN's.
- Niet nakijken op 9 cijfers. Een BSN zoals 1234567 (7 cijfers) heeft wiskundig ook een elfproef — maar is geen BSN. De lengte-check is strikt.
8. Bronnen en verder lezen
- Wet algemene bepalingen burgerservicenummer (Wabb), artikel 4 en 5 — definieert het BSN als 8- of 9-cijferig uniek nummer.
- Rijksdienst voor Identiteitsgegevens — Specificatie BSN, paragraaf over de controleberekening.
- ISO/IEC 7064:2003 — internationale standaard voor checksum-algoritmen (MOD-11-2 en MOD-97-10). De elfproef is een vereenvoudigde MOD-11-variant.
- J. Verhoeff, Error detecting decimal codes, Mathematical Centre Tract 29, Amsterdam 1969 — oorsprong van het Verhoeff-algoritme.
Heb je een fout gevonden in dit artikel of een aanvullende bron? Laat het weten.
Gerelateerde tools
- BSN Generator — genereer fictieve BSN-nummers die de elfproef doorstaan
- BSN Validator — test je eigen invoer tegen de elfproef
- IBAN MOD-97 implementeren — de zustercontrole voor bankrekeningnummers