Category Archives: Programmierung

Sainte-Laguë/Schepers Verfahren

Für Sitzverteilung im EU Parlament

Diesen Montag hatte ich ein Gespräch mit Boris Biba über Discord. Genau wie ich auch führt er einen Blog und wir hatten nun schon längere Zeit im Sinn, mal eine Kooperation zu machen. Im Gegensatz zu meinem Blog liegt sein Fokus auf Philosophie und Politik, wobei letzteres Thema natürlich perfekt dazu geeignet ist, mal etwas mit Zahlen zu jonglieren. Und so bot sich die kommende EU Wahl natürlich gerade zu an.
Unsere erste Idee war, etwas über den Wahl-O-Maten zu machen. Gut, dass dies nicht unsere letzte Idee war, denn wir ihr vielleicht wisst, wurde der Wahl-O-Mat heute vorrübergehend offline genommen wegen einer Klage.
Als nächstes erwähnte Boris, dass er gerne einen Post über die Sitzeverteilung für die Stimmen deutscher Wähler im EU Parlament machen wolle. Die dafür benutzte Methode heißt Sainte-Laguë/Schepers Verfahren und ist zu meinem Vorteil ein einfach zu implementierender Algorithmus.

Also vergiss nicht den Post von Boris, den du hier finden kannst, auch zu lesen!

Mehr Selfies von Otten heißt mehr Selfies mit CC Lizenz, wie dieses! 😀

Was ich in meinem Teil tun werde, ist das Sainte-Laguë/Schepers Verfahren zu erläutern und anschließend anhand eines anschaulichen Beispieles zu verdeutlichen. Zuvor werde ich selbstverständlich die Parteien, welche in diesem imaginären Beispiel antreten, auch vorstellen anhand von “Wahlpostern”, welche ich mit Adobe Spark erstellt habe.

Als Grundlage für diesen Post nahm ich den folgenden Artikel aus dem deutschen Wahl-Lexikon.

Beschreibung des Verfahrens

Im Grunde genommen gibt es drei verschiedene Varianten der Methode, welche jedoch alle dasgleiche Ergebnis liefern.
Zwei dieser Varianten arbeiten durch Rangfolgen. Die Andere, welche auch de facto für die deutsche Stimmenauswertung verwendet wird, basiert auf schnöder Division. Sie wird iteratives oder Divisor Verfahren genannt. Erstere Bezeichnung lässt natürlich gleich das Informatikerherz höher schlagen.

Grundsätzlich ist die Idee hinter Divisor Methode einfach, einen Divisor (Eine Zahl :P) zu finden, welcher die Stimmen aller Parteien so teilt, dass man am Ende auf die zuvergebenden Sitze kommt. Da natürlich keine halben Sitzen vergeben werden, müssen die Zahlen gerundet werden.

Im ersten Schritt die totale Anzahl an Stimmen wird durch die Anzahl an zu vergebenden Sitzen geteilt. Der Divisor kann, muss aber nicht, gerundet werden.

\(
divisor = \frac{\#Stimmen}{\#Sitze}
\)

Anschließend wird für jede Partei die Anzahl an Stimmen durch den Divisor geteilt und gerundet.

\(
SitzePartei_{i} = \frac{\#StimmenPartei_{i}}{divisor}
\)

Und wenn nun die Summe der Sitze aller Parteien schon den zu vergebenden Sitzen entspricht, sind wir fertig!
Falls nicht müssen wir den Divisor noch erhöhen oder verringern abhängig davon, ob wir zu wenige oder zu viele Sitze vergeben haben.

Ganz einfach… Wenn der Divisor größer wird, verringert sich die Anzahl an Sitzen und umgekehrt wenn er kleiner wird, erhöht sich die Anzahl an Sitzen.
Warum wir diese Regulierung aber überhaupt machen müssen, liegt daran, dass wir runden.

Nachdem wir den passenden Divisor gefunden haben, können wir die Rechnung nochmal durchführen und erhalten somit unsere passende Sitzeverteilung.

Wer wäre nicht stolz auf so ein farbenfrohes Gefieder?

Implementation des Sainte-Laguë/Schepers Verfahrens

Aber ich als Informatiker muss diese Methode natürlich auch implementieren!! Also los geht’s.

seatAllocation <- function(votes, seats){
    ## calculate the initial divisor
    divisor <- round(sum(votes) / seats)

    ## get the initial seats per party
    seatsPerParty <- round(votes / divisor)

    ## if they already satisfy the seats to be assigned, return the seat allocation
    if(sum(seatsPerParty) == seats){
        return(list(divisor = divisor, seatsPerParty = seatsPerParty))
    }

    ## otherwise increment or decrement the divisor until 
    ## the result fits and then return it
    if(sum(seatsPerParty) < seats){
        while(sum(seatsPerParty) < seats){
            divisor = divisor - 1
            seatsPerParty <- round(votes / divisor)
        }
        return(list(divisor = divisor, seatsPerParty = seatsPerParty))
    }else{
         while(sum(seatsPerParty) > seats){
            divisor = divisor + 1
            seatsPerParty <- round(votes / divisor)
        }
        return(list(divisor = divisor, seatsPerParty = seatsPerParty))
    }

}

Im Grunde genommen tut die Funktion genau das Gleiche, was ich im Fließtext davor beschrieb. Für die Kommentare auf Englisch entschuldige ich mich nicht. Kommentare in Code auf Deutsch zu schreiben ist nämlich frevel. 😛
Falls doch irgendwelche Fragen bestehen, könnt ihr sie mir schreiben.

Schwaches Wahlprogramm oder Marketing Gag? Hmmmm…

Beispiel mit Sainte-Laguë/Schepers Verfahren

Eine implementierte Methode will natürlich auch immer getestest werden. Hierfür hab ich mir ein imaginäres Wahlergebnis aus meinem nicht-existenten Hut gezogen, welches ich euch anhand eines Tortendiagrammes zeigen werde!

votes <- c(AP = 11345, CVP = 563342, EP = 618713, OSP = 305952, PDP = 95001)
votesSorted <- sort(votes, decreasing = TRUE)
names(votesSorted) <- paste0(names(votesSorted), " (", 
                               format(votesSorted/sum(votesSorted) * 100, digits = 2), "%)")
pie(votesSorted)
Pie chart of the voting result

Nun können wir die Funktion auf dieses Ergebnis loslassen!

result <- seatAllocation(votes, 310)

Selbstverständlich muss auch dieses Ergebnis wieder visualisiert werden… Dieses Mal jedoch nicht als Tortendiagramm. Denn um ehrlich zu sein, sind Tortendiagramme schwer zu lesen und manchmal auch irreführend.
Deshalb werden wir hierfür ein Waffeldiagramm benutzen, welches die tatsächlichen Sitze als Quadrate anzeigt.
Auch dieses Mal ist etwas Preprozessierung der Daten notwendig.

seatsPerParty <- result$seatsPerParty
seatsPerParty <- sort(seatsPerParty, decreasing = TRUE)
names(seatsPerParty) <- paste0(names(seatsPerParty), " (", 
                               format(seatsPerParty/sum(seatsPerParty) * 100, digits = 2), "%)")
waffle::waffle(seatsPerParty)
Waffle diagram for the seat allocation

Im Vergeich zu dem vorangegangenem Diagramm gibt es kleine Untetrschiede. Aber das ist zu erwarten, da man einen Sitz nicht zwischen Parteien aufteilen kann.

Ausblick

Natürlich gibt es noch viele weitere Verfahren zur Sitzverteilung. Einige, die äquivalent zu dem Sainte-Laguë/Schepers Verfahren sind und einige, die ein anderes Ergebnis liefern. Und in der EU werden für andere Länder auch teilweise andere Methoden angewant. Also wäre es vielleicht durchaus auch interessant über Diese jeweils einen Post zu machen.
Und dann wäre es natürlich desweiteren möglich, diese Methoden miteinander zu vergleichen. Ich würde mich über einen Kommentar freuen, wenn an diesem Thema ein größeres Interesse besteht! 🙂
Und da das mein erster Post ist, welchen ich auch auf Deutsch geschrieben habe, würde ich mich dahingehend auch über eine Rückmeldung freuen.

Please follow and like us: