def relative_superiority(m_votes, v_total_seats, v_party_seats, m_prior_allocations, divisor_gen, threshold=None, **kwargs): """Apportion by Þorkell Helgason's Relative Superiority method""" m_allocations = deepcopy(m_prior_allocations) num_allocated = sum([sum(x) for x in m_allocations]) num_total_seats = sum(v_total_seats) for n in range(num_total_seats - num_allocated): m_votes = threshold_elimination_constituencies(m_votes, 0.0, v_party_seats, m_allocations) superiority = [] first_in = [] for c in range(len(m_votes)): seats_left = v_total_seats[c] - sum(m_allocations[c]) if not seats_left: superiority.append(0) first_in.append(0) continue # Find the party next in line in the constituency: next_alloc_num = sum(m_allocations[c]) + 1 alloc_next, div_next = apportion1d(m_votes[c], next_alloc_num, m_allocations[c], divisor_gen) diff = [ alloc_next[p] - m_allocations[c][p] for p in range(len(m_votes[c])) ] next_in = diff.index(1) first_in.append(next_in) # Calculate continuation: _, div_after = apportion1d(m_votes[c], v_total_seats[c] + 1, m_allocations[c], divisor_gen) # Calculate relative superiority try: rs = float(div_next[2]) / div_after[2] except ZeroDivisionError: # If the next party is last possible, it must get the seat rs = 1000000 superiority.append(rs) # Allocate seat in constituency where the calculated # relative superiority is highest: greatest = max(superiority) idx = superiority.index(greatest) m_allocations[idx][first_in[idx]] += 1 return m_allocations, None
def relative_superiority(m_votes, v_const_seats, v_party_seats, m_prior_allocations, divisor_gen, threshold=None, **kwargs): """Apportion by Þorkell Helgason's Relative Superiority method""" m_allocations = deepcopy(m_prior_allocations) num_preallocated_seats = sum([sum(x) for x in m_allocations]) num_total_seats = sum(v_const_seats) for n in range(num_total_seats - num_preallocated_seats): m_votes = threshold_elimination_constituencies(m_votes, 0.0, v_party_seats, m_allocations) superiority = [] firstin = [] for j in range(len(m_votes)): seats_left = v_const_seats[j] - sum(m_allocations[j]) if not seats_left: superiority.append(0) firstin.append(0) continue next_alloc_num = sum(m_allocations[j]) + 1 app_next = apportion1d(m_votes[j], next_alloc_num, m_allocations[j], divisor_gen) change = [ 0 if app_next[0][i] == m_allocations[j][i] else 1 for i in range(len(m_votes[j])) ] nextin = change.index(1) new_votes = copy(m_votes[j]) new_votes[nextin] = app_next[0][2] firstin.append(nextin) # Create a provisional allocation where nextin gets the seat: v_prov_allocations = copy(m_allocations[j]) v_prov_allocations[nextin] += 1 # Calculate continuation: app_after = apportion1d(new_votes, v_const_seats[j] + 1, v_prov_allocations, divisor_gen) # Calculate relative superiority try: rs = float(app_next[1][2]) / app_after[1][2] except ZeroDivisionError: rs = 0 superiority.append(rs) greatest = max(superiority) idx = superiority.index(greatest) m_allocations[idx][firstin[idx]] += 1 return m_allocations
def party_step(v_votes, party_id, v_const_multipliers, v_party_multipliers): num_total_seats = v_party_seats[party_id] pm = party_multiplier = v_party_multipliers[party_id] # v_scaled_votes = [ a / (b * pm) if b != 0 else None for a, b in zip(v_votes, v_const_multipliers) ] v_priors = [ m_prior_allocations[x][party_id] for x in range(len(m_prior_allocations)) ] alloc, div = apportion1d(v_scaled_votes, num_total_seats, v_priors, divisor_gen) minval = div[2] maxval = max([ float(a) / b if a is not None else 0 for a, b in zip(v_scaled_votes, div[0]) ]) party_multiplier = (minval + maxval) / 2 # TODO: Make pretty-print intermediate tables on --debug #print "Party %d" % (party_id) #print " - Divisors: ", div[0] #print " - Scaled: ", v_scaled_votes #print " - Min: ", minval #print " - Max: ", maxval #print " - New const multiplier: ", party_multiplier #print " - Allocations: ", alloc return alloc, party_multiplier
def const_step(v_votes, const_id, v_const_multipliers, v_party_multipliers): num_total_seats = v_const_seats[const_id] cm = const_multiplier = v_const_multipliers[const_id] # See IV.3.5 in paper: v_scaled_votes = [ a / (b * cm) if b * cm != 0 else 0 for a, b in zip(v_votes, v_party_multipliers) ] v_priors = m_prior_allocations[const_id] alloc, div = apportion1d(v_scaled_votes, num_total_seats, v_priors, divisor_gen) # See IV.3.9 in paper: minval = div[ 2] # apportion1d gives us the last used value, which is min maxval = max([ float(a) / b if a is not 0 and a is not None else 0 for a, b in zip(v_scaled_votes, div[0]) ]) const_multiplier = (minval + maxval) / 2 # TODO: Make pretty-print intermediate tables on --debug # Results -- kind of #print "Constituency %d" % (const_id) #print " - Divisors: ", div[0] #print " - Scaled: ", v_scaled_votes #print " - Min: ", minval #print " - Max: ", maxval #print " - New const multiplier: ", const_multiplier #print " - Allocations: ", alloc return alloc, const_multiplier
def run_primary_apportionment(self): """Conduct primary apportionment""" if self.rules["debug"]: print(" + Primary apportionment") gen = self.rules.get_generator("primary_divider") const_seats = self.rules["constituency_seats"] parties = self.rules["parties"] m_allocations = [] self.last = [] for i in range(len(const_seats)): num_seats = const_seats[i] if num_seats != 0: alloc, div = apportion1d(self.m_votes[i], num_seats, [0] * len(parties), gen) self.last.append(div[2]) else: alloc = [0] * len(parties) self.last.append(0) m_allocations.append(alloc) # self.order.append(seats) # Useful: # print tabulate([[parties[x] for x in y] for y in self.order]) v_allocations = [sum(x) for x in zip(*m_allocations)] self.m_const_seats_alloc = m_allocations self.v_const_seats_alloc = v_allocations
def run_determine_adjustment_seats(self): """Calculate the number of adjustment seats each party gets.""" if self.rules["debug"]: print(" + Determine adjustment seats") v_votes = self.v_votes_eliminated gen = self.rules.get_generator("adj_determine_divider") v_priors = self.v_const_seats_alloc v_seats, _ = apportion1d(v_votes, self.total_seats, v_priors, gen) self.v_adjustment_seats = v_seats return v_seats
def relative_inferiority(m_votes, v_const_seats, v_party_seats, m_prior_allocations, divisor_gen, threshold=None, **kwargs): """Apportion by Þorkell Helgason's Relative Inferiority method.""" assert ("last" in kwargs) last = kwargs["last"] m_allocations = deepcopy(m_prior_allocations) num_allocated = sum([sum(x) for x in m_allocations]) num_total_seats = sum(v_const_seats) for n in range(num_total_seats - num_allocated): m_votes = threshold_elimination_constituencies(m_votes, 0.0, v_party_seats, m_allocations) inferiority = [] first_in = [] next_used = [] for j in range(len(m_votes)): seats_left = v_const_seats[j] - sum(m_allocations[j]) if not seats_left: inferiority.append(10000000) first_in.append(0) next_used.append(0) continue # Find the party next in line in the constituency: next_alloc_num = sum(m_allocations[j]) + 1 alloc_next, div = apportion1d(m_votes[j], next_alloc_num, m_allocations[j], divisor_gen) diff = [ alloc_next[i] - m_allocations[j][i] for i in range(len(m_votes[j])) ] next_in = diff.index(1) first_in.append(next_in) next_used.append(div[2]) # Calculate relative inferiority: ri = float(last[j]) / next_used[j] inferiority.append(ri) # Allocate seat in constituency where the calculated # relative inferiority is lowest: least = min(inferiority) idx = inferiority.index(least) m_allocations[idx][first_in[idx]] += 1 last[idx] = next_used[idx] return m_allocations, None
def nearest_neighbor(m_votes, v_total_seats, v_party_seats, m_prior_allocations, divisor_gen, threshold=None, **kwargs): assert ("last" in kwargs) last = kwargs["last"] m_allocations = deepcopy(m_prior_allocations) num_allocated = sum([sum(x) for x in m_allocations]) num_total_seats = sum(v_total_seats) for n in range(num_total_seats - num_allocated): m_votes = threshold_elimination_constituencies(m_votes, 0.0, v_party_seats, m_allocations) neighbor_ratio = [] first_in = [] next_used = [] for c in range(len(m_votes)): seats_left = v_total_seats[c] - sum(m_allocations[c]) if not seats_left: neighbor_ratio.append(10000000) first_in.append(0) next_used.append(0) continue # Find the party next in line in the constituency: next_alloc_num = sum(m_allocations[c]) + 1 alloc_next, div = apportion1d(m_votes[c], next_alloc_num, m_allocations[c], divisor_gen) diff = [ alloc_next[p] - m_allocations[c][p] for p in range(len(m_votes[c])) ] next_in = diff.index(1) first_in.append(next_in) next_used.append(div[2]) # Calculate neighbor ratio: nr = float(last[c]) / next_used[c] neighbor_ratio.append(nr) # Allocate seat in constituency where the calculated # neighbor ratio is lowest: least = min(neighbor_ratio) idx = neighbor_ratio.index(least) m_allocations[idx][first_in[idx]] += 1 last[idx] = next_used[idx] return m_allocations, None
def party_step(v_votes, party_id, const_multipliers, party_multipliers): num_total_seats = v_party_seats[party_id] pm = party_multiplier = party_multipliers[party_id] v_scaled_votes = [a/(b*pm) if b != 0 else 0 for a, b in zip(v_votes, const_multipliers)] v_priors = [const_alloc[party_id] for const_alloc in m_allocations] alloc, div = apportion1d(v_scaled_votes, num_total_seats, v_priors, divisor_gen) minval = div[2] maxval = max([float(a)/b for a, b in zip(v_scaled_votes, div[0])]) party_multiplier = (minval+maxval)/2 return party_multiplier, alloc
def const_step(v_votes, const_id, const_multipliers, party_multipliers): num_total_seats = v_total_seats[const_id] cm = const_multiplier = const_multipliers[const_id] # See IV.3.5 in paper: v_scaled_votes = [a/(b*cm) if b*cm != 0 else 0 for a, b in zip(v_votes, party_multipliers)] v_priors = m_allocations[const_id] alloc, div = apportion1d(v_scaled_votes, num_total_seats, v_priors, divisor_gen) # See IV.3.9 in paper: minval = div[2] # apportion1d gives the last used value, which is min maxval = max([float(a)/b for a, b in zip(v_scaled_votes, div[0])]) const_multiplier = (minval+maxval)/2 return const_multiplier, alloc
def relative_inferiority(m_votes, v_const_seats, v_party_seats, m_prior_allocations, divisor_gen, threshold=None, **kwargs): """ Apportion by Þorkell Helgason's Relative Inferiority method. This method is incomplete. """ m_allocations = copy(m_prior_allocations) m_max_seats = [[min(Ci, Pj) for Pj in v_party_seats] for Ci in v_const_seats] # Probably not needed: const_filled = [False] * len(v_const_seats) party_filled = [False] * len(v_party_seats) # num_allocated = for i in range(10): for i in range(len(v_const_seats)): app = apportion1d(m_votes[i], 10, m_allocations[i], divisor_gen) return m_allocations
def var_alt_scal(m_votes, v_total_seats, v_party_seats, m_prior_allocations, divisor_gen, threshold, **kwargs): """ # Implementation of the Alternating-Scaling algorithm. Inputs: - m_votes: A matrix of votes (rows: constituencies, columns: parties) - v_const_seats: A vector of total seats in each constituency - v_party_seats: A vector of seats allocated to parties - m_prior_allocations: A matrix of where parties have previously gotten seats - divisor_gen: A generator function generating divisors, e.g. d'Hondt - threshold: A cutoff threshold for participation. """ m_allocations = deepcopy(m_prior_allocations) def const_step(v_votes, const_id, const_multipliers, party_multipliers): num_total_seats = v_total_seats[const_id] cm = const_multiplier = const_multipliers[const_id] # See IV.3.5 in paper: v_scaled_votes = [a/(b*cm) if b*cm != 0 else 0 for a, b in zip(v_votes, party_multipliers)] v_priors = m_allocations[const_id] alloc, div = apportion1d(v_scaled_votes, num_total_seats, v_priors, divisor_gen) # See IV.3.9 in paper: minval = div[2] # apportion1d gives the last used value, which is min maxval = max([float(a)/b for a, b in zip(v_scaled_votes, div[0])]) const_multiplier = (minval+maxval)/2 return const_multiplier, alloc def party_step(v_votes, party_id, const_multipliers, party_multipliers): num_total_seats = v_party_seats[party_id] pm = party_multiplier = party_multipliers[party_id] v_scaled_votes = [a/(b*pm) if b != 0 else 0 for a, b in zip(v_votes, const_multipliers)] v_priors = [const_alloc[party_id] for const_alloc in m_allocations] alloc, div = apportion1d(v_scaled_votes, num_total_seats, v_priors, divisor_gen) minval = div[2] maxval = max([float(a)/b for a, b in zip(v_scaled_votes, div[0])]) party_multiplier = (minval+maxval)/2 return party_multiplier, alloc num_constituencies = len(m_votes) num_parties = len(m_votes[0]) const_multipliers = [1] * num_constituencies party_multipliers = [1] * num_parties step = 0 while step < 100: # Constituency step: c_muls = [] const_allocs = [] for c in range(num_constituencies): mul, alloc = const_step(m_votes[c], c, const_multipliers, party_multipliers) const_multipliers[c] *= mul c_muls.append(mul) const_allocs.append(alloc) # Party step: p_muls = [] party_allocs = [] for p in range(num_parties): vp = [v[p] for v in m_votes] mul, alloc = party_step(vp, p, const_multipliers, party_multipliers) party_multipliers[p] *= mul p_muls.append(mul) party_allocs.append(alloc) step += 1 # Stop when constituency step and party step give the same result done = all([const_allocs[i][j] == party_allocs[j][i] for i in range(len(const_allocs)) for j in range(len(party_allocs))]) if done: break # Finally, use party_multipliers and const_multipliers to arrive at # final apportionment: results = [] for c in range(num_constituencies): num_total_seats = v_total_seats[c] cm = const_multipliers[c] v_scaled_votes = [a/(b*cm) if b*cm != 0 else 0 for a, b in zip(m_votes[c], party_multipliers)] v_priors = m_allocations[c] alloc, _ = apportion1d(v_scaled_votes, num_total_seats, v_priors, divisor_gen) results.append(alloc) return results, None
def alternating_scaling(m_votes, v_const_seats, v_party_seats, m_prior_allocations, divisor_gen, threshold, **kwargs): """ # Implementation of the Alternating-Scaling algorithm. Inputs: - m_votes_orig: A matrix of votes (rows: constituencies, columns: parties) - v_const_seats: A vector of constituency seats - v_party_seats: A vector of seats allocated to parties - m_prior_allocations: A matrix of where parties have previously gotten seats - divisor_gen: A generator function generating divisors, e.g. d'Hondt - threshold: A cutoff threshold for participation. """ def const_step(v_votes, const_id, v_const_multipliers, v_party_multipliers): num_total_seats = v_const_seats[const_id] cm = const_multiplier = v_const_multipliers[const_id] # See IV.3.5 in paper: v_scaled_votes = [ a / (b * cm) if b * cm != 0 else 0 for a, b in zip(v_votes, v_party_multipliers) ] v_priors = m_prior_allocations[const_id] alloc, div = apportion1d(v_scaled_votes, num_total_seats, v_priors, divisor_gen) # See IV.3.9 in paper: minval = div[ 2] # apportion1d gives us the last used value, which is min maxval = max([ float(a) / b if a is not 0 and a is not None else 0 for a, b in zip(v_scaled_votes, div[0]) ]) const_multiplier = (minval + maxval) / 2 # TODO: Make pretty-print intermediate tables on --debug # Results -- kind of #print "Constituency %d" % (const_id) #print " - Divisors: ", div[0] #print " - Scaled: ", v_scaled_votes #print " - Min: ", minval #print " - Max: ", maxval #print " - New const multiplier: ", const_multiplier #print " - Allocations: ", alloc return alloc, const_multiplier def party_step(v_votes, party_id, v_const_multipliers, v_party_multipliers): num_total_seats = v_party_seats[party_id] pm = party_multiplier = v_party_multipliers[party_id] # v_scaled_votes = [ a / (b * pm) if b != 0 else None for a, b in zip(v_votes, v_const_multipliers) ] v_priors = [ m_prior_allocations[x][party_id] for x in range(len(m_prior_allocations)) ] alloc, div = apportion1d(v_scaled_votes, num_total_seats, v_priors, divisor_gen) minval = div[2] maxval = max([ float(a) / b if a is not None else 0 for a, b in zip(v_scaled_votes, div[0]) ]) party_multiplier = (minval + maxval) / 2 # TODO: Make pretty-print intermediate tables on --debug #print "Party %d" % (party_id) #print " - Divisors: ", div[0] #print " - Scaled: ", v_scaled_votes #print " - Min: ", minval #print " - Max: ", maxval #print " - New const multiplier: ", party_multiplier #print " - Allocations: ", alloc return alloc, party_multiplier num_constituencies = len(m_votes) num_parties = len(m_votes[0]) const_multipliers = [1] * num_constituencies party_multipliers = [1] * num_parties step = 0 const_done = False party_done = False while step < 200: step += 1 if step % 2 == 1: #Constituency step: muls = [] for c in range(num_constituencies): alloc, mul = const_step(m_votes[c], c, const_multipliers, party_multipliers) const_multipliers[c] *= mul muls.append(mul) const_done = all([round(x, 5) == 1.0 or x == 500000 for x in muls]) else: # print "== Party step %d ==" % step muls = [] for p in range(num_parties): vp = [v[p] for v in m_votes] alloc, mul = party_step(vp, p, const_multipliers, party_multipliers) party_multipliers[p] *= mul muls.append(mul) party_done = all([round(x, 5) == 1.0 or x == 500000 for x in muls]) if const_done and party_done: break # Finally, use party_multipliers and const_multipliers to arrive at # final apportionment: results = [] for c in range(num_constituencies): num_total_seats = v_const_seats[c] cm = const_multipliers[c] v_scaled_votes = [ a / (b * cm) if b != 0 else None for a, b in zip(m_votes[c], party_multipliers) ] v_priors = m_prior_allocations[c] alloc, div = apportion1d(v_scaled_votes, num_total_seats, v_priors, divisor_gen) results.append(alloc) return results
def switching(m_votes, v_total_seats, v_party_seats, m_prior_allocations, divisor_gen, threshold=None, orig_votes=None, **kwargs): v_prior_allocations = [sum(x) for x in zip(*m_prior_allocations)] # The number of adjustment seats each party should receive: correct_adj_seats = [ v_party_seats[p] - v_prior_allocations[p] for p in range(len(v_party_seats)) ] # Allocate adjustment seats as if they were constituency seats m_adj_seats = [] for c in range(len(m_prior_allocations)): votes = [ m_votes[c][p] if correct_adj_seats[p] > 0 else 0 for p in range(len(m_votes[c])) ] alloc, div = apportion1d(votes, v_total_seats[c], m_prior_allocations[c], divisor_gen) adj_seats = [ alloc[p] - m_prior_allocations[c][p] for p in range(len(alloc)) ] m_adj_seats.append(adj_seats) # Transfer adjustment seats within constituencies from parties that have # too many seats to parties that have too few seats, prioritized by # "sensitivity", until all parties have the correct number of seats # or no more swaps can be made: done = False while not done: v_adj_seats = [sum(x) for x in zip(*m_adj_seats)] diff_party = [ v_adj_seats[p] - correct_adj_seats[p] for p in range(len(v_adj_seats)) ] over = [i for i in range(len(diff_party)) if diff_party[i] > 0] under = [i for i in range(len(diff_party)) if diff_party[i] < 0] sensitivity = [] for i in range(len(m_votes)): for j in over: for k in under: if m_adj_seats[i][j] != 0 and m_votes[i][k] != 0: gen_j = divisor_gen() j_seats = m_prior_allocations[i][j] + m_adj_seats[i][j] for x in range(j_seats): div_j = next(gen_j) gen_k = divisor_gen() k_seats = m_prior_allocations[i][k] + m_adj_seats[i][k] for x in range(k_seats + 1): div_k = next(gen_k) s = (m_votes[i][j] / div_j) / (m_votes[i][k] / div_k) heapq.heappush(sensitivity, (s, (i, j, k))) done = len(sensitivity) == 0 if not done: # Find the constituency and pair of parties with the lowest # sensitivity, and transfer a seat: i, j, k = heapq.heappop(sensitivity)[1] m_adj_seats[i][j] -= 1 m_adj_seats[i][k] += 1 m_allocations = [[ m_prior_allocations[c][p] + m_adj_seats[c][p] for p in range(len(m_adj_seats[c])) ] for c in range(len(m_adj_seats))] return m_allocations, None
def icelandic_apportionment(m_votes, v_total_seats, v_party_seats, m_prior_allocations, divisor_gen, threshold=None, orig_votes=None, **kwargs): """ Apportion based on Icelandic law nr. 24/2000. """ m_allocations = deepcopy(m_prior_allocations) # 2.1. # (Deila skal í atkvæðatölur samtakanna með tölu kjördæmissæta þeirra, # fyrst að viðbættum 1, síðan 2, þá 3 o.s.frv. Útkomutölurnar nefnast # landstölur samtakanna.) v_seats = [sum(x) for x in zip(*m_prior_allocations)] v_votes = [sum(x) for x in zip(*m_votes)] num_allocated = sum(v_seats) total_seats = sum(v_total_seats) # 2.2. # (Taka skal saman skrá um þau tvö sæti hvers framboðslista sem næst # komust því að fá úthlutun í kjördæmi skv. 107. gr. Við hvert # þessara sæta skal skrá hlutfall útkomutölu sætisins skv. 1. tölul. # 107. gr. af öllum gildum atkvæðum í kjördæminu.) # Create list of 2 top seats on each remaining list that almost got in. # 2.7. # (Beita skal ákvæðum 3. tölul. svo oft sem þarf þar til lokið er # úthlutun allra jöfnunarsæta, sbr. 2. mgr. 8. gr.) invalid = [] v_last_alloc = deepcopy(v_seats) seats_info = [] while num_allocated < total_seats: alloc, d = apportion1d(v_votes, num_allocated+1, v_last_alloc, divisor_gen, invalid) # 2.6. # (Hafi allar hlutfallstölur stjórnmálasamtaka verið numdar brott # skal jafnframt fella niður allar landstölur þeirra.) diff = [alloc[j]-v_last_alloc[j] for j in range(len(alloc))] idx = diff.index(1) v_proportions = [] for const in range(len(m_votes)): const_votes = orig_votes[const] s = sum(const_votes) div = divisor_gen() for i in range(m_allocations[const][idx]+1): x = next(div) p = (float(const_votes[idx])/s)/x v_proportions.append(p) # 2.5. # (Þegar lokið hefur verið að úthluta jöfnunarsætum í hverju # kjördæmi skv. 2. mgr. 8. gr. skulu hlutfallstölur allra # lista í því kjördæmi felldar niður.) if sum(m_allocations[const]) == v_total_seats[const]: v_proportions[const] = 0 # 2.3. # (Finna skal hæstu landstölu skv. 1. tölul. sem hefur ekki þegar # verið felld niður. Hjá þeim stjórnmálasamtökum, sem eiga þá # landstölu, skal finna hæstu hlutfallstölu lista skv. 2. tölul. # og úthluta jöfnunarsæti til hans. Landstalan og hlutfallstalan # skulu síðan báðar felldar niður.) if max(v_proportions) != 0: const = [j for j,k in enumerate(v_proportions) if k == max(v_proportions)] if len(const) > 1: # 2.4. # (Nú eru tvær eða fleiri lands- eða hlutfallstölur jafnháar # þegar að þeim kemur skv. 3. tölul. og skal þá hluta um röð # þeirra.) const = [random.choice(const)] m_allocations[const[0]][idx] += 1 num_allocated += 1 v_last_alloc = alloc seats_info.append((const[0], d[2], idx, v_proportions[const[0]])) else: invalid.append(idx) return m_allocations, seats_info
def icelandic_apportionment(m_votes, v_const_seats, v_party_seats, m_prior_allocations, divisor_gen, threshold=None, orig_votes=None, **kwargs): """ Apportion based on Icelandic law nr. 24/2000. """ m_allocations = deepcopy(m_prior_allocations) # 2.1 # (Deila skal í atkvæðatölur samtakanna með tölu kjördæmissæta þeirra, # fyrst að viðbættum 1, síðan 2, þá 3 o.s.frv. Útkomutölurnar nefnast # landstölur samtakanna.) v_seats = [sum(x) for x in zip(*m_prior_allocations)] v_votes = [sum(x) for x in zip(*m_votes)] num_allocated = sum(v_seats) num_missing = sum(v_const_seats) - num_allocated # 2.2. Create list of 2 top seats on each remaining list that almost got in. # (Taka skal saman skrá um þau tvö sæti hvers framboðslista sem næst # komust því að fá úthlutun í kjördæmi skv. 107. gr. Við hvert # þessara sæta skal skrá hlutfall útkomutölu sætisins skv. 1. tölul. # 107. gr. af öllum gildum atkvæðum í kjördæminu.) # 2.7. # (Beita skal ákvæðum 3. tölul. svo oft sem þarf þar til lokið er # úthlutun allra jöfnunarsæta, sbr. 2. mgr. 8. gr.) v_last_alloc = deepcopy(v_seats) for i in range(num_allocated + 1, num_allocated + num_missing + 1): alloc, div = apportion1d(v_votes, i, v_last_alloc, divisor_gen) # 2.6. # (Hafi allar hlutfallstölur stjórnmálasamtaka verið numdar brott # skal jafnframt fella niður allar landstölur þeirra.) diff = [alloc[i] - v_last_alloc[i] for i in range(len(alloc))] idx = diff.index(1) v_last_alloc = alloc m_proportions = [] for cons in range(len(m_votes)): cons_votes = orig_votes[cons] s = sum(cons_votes) proportions = [] for party in range(len(m_votes[0])): d = divisor_gen() for j in range(m_allocations[cons][party] + 1): x = next(d) k = (float(orig_votes[cons][party]) / s) / x proportions.append(k) m_proportions.append(proportions) # 2.5. # (Þegar lokið hefur verið að úthluta jöfnunarsætum í hverju # kjördæmi skv. 2. mgr. 8. gr. skulu hlutfallstölur allra # lista í því kjördæmi felldar niður.) if sum(m_allocations[cons]) == v_const_seats[cons]: # print "Done allocating in constituency %d" % (cons) m_proportions[cons] = [0] * len(v_seats) # 2.3. # (Finna skal hæstu landstölu skv. 1. tölul. sem hefur ekki þegar # verið felld niður. Hjá þeim stjórnmálasamtökum, sem eiga þá # landstölu, skal finna hæstu hlutfallstölu lista skv. 2. tölul. # og úthluta jöfnunarsæti til hans. Landstalan og hlutfallstalan # skulu síðan báðar felldar niður.) w = [m_proportions[i][idx] for i in range(len(m_proportions))] # print "Proportions for %d: %s" % (idx, w) const = [i for i, j in enumerate(w) if j == max(w)] if len(const) > 1: # 2.4. # (Nú eru tvær eða fleiri lands- eða hlutfallstölur jafnháar # þegar að þeim kemur skv. 3. tölul. og skal þá hluta um röð # þeirra.) const = [random.choice(const)] m_allocations[const[0]][idx] += 1 return m_allocations