; XEmacs: This file contains -*- Scheme -*- source code. ;;; Voting power ;;; John David Stone ;;; Department of Mathematics and Computer Science ;;; Grinnell College ;;; stone@cs.grinnell.edu ;;; created May 11, 2000 ;;; last revised May 25, 2000 ;;; In presidential elections in the United States, such as the one that ;;; will be held in November of this year, the outcome is not directly ;;; determined by the numbers of ballots that individual citizens cast for ;;; the various candidates, but by the votes of members of the Electoral ;;; College. In each state, the persons who are elected or appointed to ;;; membership in the Electoral College are supposed to cast their votes ;;; for the candidate who received the plurality of the votes of the ;;; citizens of that state. (For example, in 1996, more Iowans voted for ;;; Bill Clinton than for any of the other presidential candidates, so the ;;; seven persons chosen as members of the Electoral College all cast their ;;; ballots for Clinton.) ;;; Because the candidate who receives the plurality of popular votes is ;;; awarded all of the state's electoral votes, regardless of the margin of ;;; victory, it is widely believed that the Electoral College system is ;;; biased towards populous states. Here's the reasoning: It is far more ;;; likely that the outcome of the presidential election will depend ;;; critically on which candidate wins a plurality in, say, California ;;; (which chooses fifty-four members of the Electoral College) than on the ;;; outcome in, say, Vermont (which chooses only three). The effect of bloc ;;; voting by state in the Electoral College, therefore, is that a voter in ;;; California is far more likely to participate in determining the outcome ;;; of the election than a voter in Vermont. ;;; On the other hand, skeptics have argued that the Electoral College ;;; system actually favors less populous states, for two reasons: (1) The ;;; Electoral College representation of a state is not directly ;;; proportional to the state's population, but rather is set equal to the ;;; size of the state's Congressional delegation. Since each state, no ;;; matter how small, sends two senators to Congress, small states choose ;;; more members of the Electoral College per capita than large ones. ;;; (2) Although achieving a plurality in a larger state causes a bigger ;;; swing in the Electoral College, the larger number of voters in a larger ;;; state makes it more difficult to win a plurality. Winning several small ;;; states, therefore, is a task that is comparable in difficulty to ;;; winning one large state. ;;; The purpose of this program is to develop some evidence relevant to ;;; this dispute by estimating the voting power of an individual voter in ;;; each state -- the likelihood that that individual's vote has a share in ;;; determining the outcome of the election. ;;; We'll represent each state by a record containing four fields: the name ;;; of the state, the number of electors it chooses, the number of ;;; registered voters in that state, and an index of the voting power of ;;; each individual registered voter in the state. ;;; The RECORD-BUILDER metaprogram produced the following definition of ;;; this record. (define produce-state-mark (let ((type-mark (list (quote state)))) (lambda () type-mark))) (define make-state (lambda (name electors registered-voters voting-power-index) (let ((result (make-vector 5))) (vector-set! result 0 (produce-state-mark)) (state-name-set! result name) (state-electors-set! result electors) (state-registered-voters-set! result registered-voters) (state-voting-power-index-set! result voting-power-index) result))) (define state-name (lambda (s) (vector-ref s 1))) (define state-name-set! (lambda (s new-name) (if (string? new-name) (vector-set! s 1 new-name) (error (string-append "state-name-set!" ": Precondition failed"))))) (define state-electors (lambda (s) (vector-ref s 2))) (define state-electors-set! (lambda (s new-electors) (if (positive-exact-integer? new-electors) (vector-set! s 2 new-electors) (error (string-append "state-electors-set!" ": Precondition failed"))))) (define state-registered-voters (lambda (s) (vector-ref s 3))) (define state-registered-voters-set! (lambda (s new-registered-voters) (if (positive-exact-integer? new-registered-voters) (vector-set! s 3 new-registered-voters) (error (string-append "state-registered-voters-set!" ": Precondition failed"))))) (define state-voting-power-index (lambda (s) (vector-ref s 4))) (define state-voting-power-index-set! (lambda (s new-voting-power-index) (if (positive-real? new-voting-power-index) (vector-set! s 4 new-voting-power-index) (error (string-append "state-voting-power-index-set!" ": Precondition failed"))))) (define state? (lambda (something) (and (vector? something) (= (vector-length something) 5) (eq? (vector-ref something 0) (produce-state-mark)) (string? (vector-ref something 1)) (positive-exact-integer? (vector-ref something 2)) (positive-exact-integer? (vector-ref something 3)) (positive-real? (vector-ref something 4))))) (define state=? (lambda (left right) (and (string=? (state-name left) (state-name right)) (= (state-electors left) (state-electors right)) (= (state-registered-voters left) (state-registered-voters right)) (= (state-voting-power-index left) (state-voting-power-index right))))) (define state-copy (lambda (s) (make-state (string-copy (state-name s)) (state-electors s) (state-registered-voters s) (state-voting-power-index s)))) ;;; This definition uses a couple of non-primitive but self-explanatory ;;; precondition predicates: (define positive-exact-integer? (lambda (something) (and (integer? something) (exact? something) (positive? something)))) (define positive-real? (lambda (something) (and (real? something) (positive? something)))) ;;; The estimated voting power for an individual voter in a given state is ;;; computed from the number of electors that the state chooses and the ;;; number of registered voters in the state, as follows: ;;; * If a state elects or appoints v members of the Electoral College, the ;;; contribution of that state's electors to the outcome of the ;;; presidential election is proportional to v^(3/2). ;;; * Every registered voter in a state has an equal power to determine the ;;; plurality winner in that state. The state's voting power, therefore, ;;; is divided equally among the registered voters of that state. ;;; For instance, for a state that has two million registered voters and ;;; chooses eight members of the electoral college, we'd compute the voting ;;; power of each voter as 8^(3/2)/2000000, or about 0.0000113. A voter in ;;; a state that has seven million registered voters and chooses twenty ;;; members of the electoral college would have a voting power of ;;; 20^(3/2)/7000000, or about 0.0000128. (define voting-power (lambda (electors registered-voters) (/ (expt electors 3/2) registered-voters))) ;;; The file /home/stone/courses/scheme/data/electoral.dat contains ;;; fifty-one lines, one for each state and one for the District of ;;; Columbia. Each line contains the name of the state (in the first ;;; twenty columns), the number of persons from that state the Electoral ;;; College (in the next three columns), and (in the remaining nine ;;; columns) the number of voters registered in that state at the time of ;;; the 1998 Congressional elections, as determined by the Federal Election ;;; Commission and presented in their report ``Voter registration and ;;; turnout -- 1998''. (North Dakota does not require voters to register, ;;; and in Wisconsin one registers on election day, so in both of those ;;; states the estimated voting-age population at the time of the 1998 ;;; Congressional elections is listed instead.) ;;; The READ-STATE procedure takes an already-open input port, reads in the ;;; name, number of electors, and number of registered voters, computes the ;;; voting power index, and constructs and returns a state record ;;; containing this information. (define read-state (lambda (source) (let* ((name (trim-right (read-chars source 20) #\space)) (electors (read source)) (registered-voters (read source)) (voting-power-index (voting-power electors registered-voters))) (read-char source) ; Discard the newline at the end of the line. (make-state name electors registered-voters voting-power-index)))) ;;; The READ-CHARS procedure reads in a specified number of characters ;;; through an already-open input port and returns a string made up from ;;; them. If the end-of-file object is encountered before the specified ;;; number of characters has been read, however, the string that is ;;; returned contains only the characters that are read before the ;;; encounter. (define read-chars (lambda (source number) (let kernel ((remaining number) (so-far null)) (if (or (zero? remaining) (eof-object? (peek-char source))) (list->string (reverse so-far)) (kernel (- remaining 1) (cons (read-char source) so-far)))))) ;;; The TRIM-RIGHT procedure takes a string as its first argument and a ;;; character as its second, and returns a string that is just like the ;;; first argument, except that any copies of the second character have ;;; been removed from the right end. (define trim-right (lambda (str ugh) (do ((right (string-length str) (- right 1))) ((or (zero? right) (not (char=? (string-ref str (- right 1)) ugh))) (substring str 0 right))))) ;;; Let's read in the data and store them in a list of state records. (define states (let ((source (open-input-file "/home/stone/courses/scheme/data/electoral.dat"))) (let kernel ((peek (peek-char source))) (if (eof-object? peek) (begin (close-input-port source) null) (let ((first-state (read-state source))) (cons first-state (kernel (peek-char source)))))))) ;;; The next step is to normalize the voting power indices by dividing each ;;; one by the least of them. (let ((least-voting-power-index (apply min (map state-voting-power-index states)))) (for-each (lambda (state) (state-voting-power-index-set! state (/ (state-voting-power-index state) least-voting-power-index))) states)) ;;; Next, we sort the states into descending order by voting power index. ;;; We'll use the insertion sort, from the lab on sorting methods. (define insertion-sort (lambda (may-precede?) (define insert (lambda (new-element ls) (cond ((null? ls) (list new-element)) ((may-precede? new-element (car ls)) (cons new-element ls)) (else (cons (car ls) (insert new-element (cdr ls))))))) (lambda (unsorted) (do ((rest unsorted (cdr rest)) (sorted '() (insert (car rest) sorted))) ((null? rest) sorted))))) ;;; GREATER-VOTING-POWER-INDEX? is the ordering relation that controls the ;;; sort. (define greater-voting-power-index? (lambda (left right) (>= (state-voting-power-index left) (state-voting-power-index right)))) ;;; Here's the sorting step: (define sorted-states ((insertion-sort greater-voting-power-index?) states)) ;;; The following procedure takes a state record and an already-open output ;;; port and writes out the state's name (padded on the right with spaces ;;; to a width of twenty columns) and its voting power index, adding a ;;; newline character at the end. (define write-state (lambda (state target) (display (pad-right (state-name state) 20 #\space) target) (display #\space target) (display (state-voting-power-index state) target) (newline target))) ;;; The PAD-RIGHT procedure takes as its arguments a string, a natural ;;; number indicating the desired length of the result string, and a ;;; character, returns a string like the argument string except that enough ;;; copies of the character have been appended at the right end to make the ;;; result string have the desired length. (If the length of the argument ;;; string is greater than or equal to the desired length, it is returned ;;; without change.) (define pad-right (lambda (str desired-length pad-character) (let ((len (string-length str))) (if (<= desired-length len) str (string-append str (make-string (- desired-length len) pad-character)))))) ;;; It's time to create the output file. (let ((target (open-output-file "voting-power.dat"))) (for-each (lambda (state) (write-state state target)) sorted-states) (close-output-port target))