Precompute Ballot::has_equal_rankings at parse time

Before: 5.3943 +- 0.0287
After: 5.27661 +- 0.00472
This commit is contained in:
RunasSudo 2022-08-25 21:49:50 +10:00
parent 254c04b574
commit 6bb127a124
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
3 changed files with 23 additions and 10 deletions

View File

@ -71,6 +71,7 @@ impl<N: Number> Election<N> {
let new_ballot = Ballot {
orig_value: N::one(),
preferences: ballot.preferences.clone(),
has_equal_rankings: ballot.has_equal_rankings,
};
normalised_ballots.push(new_ballot);
n += &one;
@ -81,7 +82,7 @@ impl<N: Number> Election<N> {
/// Convert ballots with equal rankings to strict-preference "minivoters"
pub fn realise_equal_rankings(&mut self) {
if !self.ballots.iter().any(|b| b.has_equal_rankings()) {
if !self.ballots.iter().any(|b| b.has_equal_rankings) {
// No equal rankings
return;
}
@ -582,17 +583,14 @@ pub struct Ballot<N> {
pub orig_value: N,
/// Indexes of candidates preferenced at each level on the ballot
pub preferences: Vec<Vec<usize>>,
/// Whether multiple candidates are preferenced at the same level
pub has_equal_rankings: bool,
}
impl<N: Number> Ballot<N> {
/// Check if this ballot has any equal rankings
pub fn has_equal_rankings(&self) -> bool {
return self.preferences.iter().any(|p| p.len() > 1);
}
/// Convert ballot with equal rankings to strict-preference "minivoters"
pub fn realise_equal_rankings_into(self, dest: &mut Vec<Ballot<N>>) {
if !self.has_equal_rankings() {
if !self.has_equal_rankings {
dest.push(self);
return;
}
@ -631,7 +629,8 @@ impl<N: Number> Ballot<N> {
for minivoter in minivoters {
dest.push(Ballot {
orig_value: weight_each.clone(),
preferences: minivoter
preferences: minivoter,
has_equal_rankings: false,
});
}
}

View File

@ -38,6 +38,9 @@ pub struct BLTParser<N: Number, I: Iterator<Item=char>> {
/// Temporary buffer for parsing ballot values
ballot_value_buf: String,
/// Whether the current ballot has equal preferences
ballot_has_equal_rankings: bool,
/// Current line number
line_no: u32,
/// Current column number
@ -139,6 +142,8 @@ impl<N: Number, I: Iterator<Item=char>> BLTParser<N, I> {
/// Parse a ballot
fn ballot(&mut self) -> Result<(), ParseError> {
self.ballot_has_equal_rankings = false;
self.ballot_value()?;
self.delimiter_not_nl();
@ -152,6 +157,8 @@ impl<N: Number, I: Iterator<Item=char>> BLTParser<N, I> {
break;
} else if self.lookahead() == '=' {
// Equal preference
self.ballot_has_equal_rankings = true;
self.accept();
preferences.last_mut().unwrap().push(self.usize()? - 1);
self.delimiter_not_nl();
@ -167,6 +174,7 @@ impl<N: Number, I: Iterator<Item=char>> BLTParser<N, I> {
let ballot = Ballot {
orig_value: N::parse(&self.ballot_value_buf),
preferences,
has_equal_rankings: self.ballot_has_equal_rankings,
};
self.election.ballots.push(ballot);
@ -376,6 +384,7 @@ impl<N: Number, I: Iterator<Item=char>> BLTParser<N, I> {
Self {
chars,
ballot_value_buf: String::new(),
ballot_has_equal_rankings: false,
line_no: 1,
col_no: 1,
num_candidates: 0,

View File

@ -95,6 +95,7 @@ pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequ
ballots.push(Ballot {
orig_value: value,
preferences: vec![],
has_equal_rankings: false,
});
continue;
}
@ -102,17 +103,20 @@ pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequ
let mut sorted_preferences = Vec::with_capacity(preferences.len());
let mut last_ranking = None;
let mut has_equal_rankings = false;
for ranking in unique_rankings {
// Filter for preferences at this ranking
let prefs_this_ranking: Vec<usize> = preferences.iter()
.filter_map(|(r, i)| if *r == ranking { Some(**i) } else { None })
.collect();
if require_strict_order {
if prefs_this_ranking.len() != 1 {
if prefs_this_ranking.len() != 1 {
if require_strict_order {
// Duplicate rankings
break;
}
has_equal_rankings = true;
}
if require_sequential {
@ -131,6 +135,7 @@ pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequ
ballots.push(Ballot {
orig_value: value,
preferences: sorted_preferences,
has_equal_rankings,
});
}