Last active
June 25, 2025 21:52
-
-
Save anoras/085b9166ca43910161fc9f3d49279e71 to your computer and use it in GitHub Desktop.
Optimised lotto winner counts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// cargo-deps: rand = "0.8", rayon = "1.8" | |
use rand::seq::SliceRandom; | |
use rand::thread_rng; | |
use rayon::prelude::*; | |
use std::time::Instant; | |
const CHUNK_SIZE: usize = 10_000_000; | |
const TOTAL_ROWS: usize = 283_000_000; | |
/// Encode 7 lotto numbers into a bitset | |
fn encode_bitset(numbers: &[u8]) -> u64 { | |
numbers.iter().fold(0u64, |acc, &n| acc | (1 << (n - 1))) | |
} | |
/// Classify a bitset into prize tier 0–5 | |
fn classify_row(row: u64, winner_main: u64, addition_bit: u64) -> usize { | |
let hits = (row & winner_main).count_ones(); | |
let has_add = (row & addition_bit) != 0; | |
match hits { | |
7 => 1, | |
6 => if has_add { 2 } else { 3 }, | |
5 => 4, | |
4 => if has_add { 5 } else { 0 }, | |
_ => 0, | |
} | |
} | |
/// Generate a chunk of random Lotto rows with reduced allocations | |
fn generate_lotto_chunk(count: usize) -> Vec<u64> { | |
let mut rng = thread_rng(); | |
let mut pool = [0u8; 34]; | |
for (i, p) in pool.iter_mut().enumerate() { | |
*p = (i + 1) as u8; | |
} | |
let mut rows = Vec::with_capacity(count); | |
for _ in 0..count { | |
pool.shuffle(&mut rng); | |
rows.push(encode_bitset(&pool[..7])); | |
} | |
rows | |
} | |
/// Process rows in parallel, count prize tiers | |
fn process_in_chunks(winner_main: u64, addition_bit: u64) -> [u64; 6] { | |
let mut total_counts = [0u64; 6]; | |
let mut processed = 0; | |
while processed < TOTAL_ROWS { | |
let batch_size = CHUNK_SIZE.min(TOTAL_ROWS - processed); | |
let chunk = generate_lotto_chunk(batch_size); | |
let counts = chunk | |
.par_iter() | |
.map(|&row| classify_row(row, winner_main, addition_bit)) | |
.fold(|| [0u64; 6], |mut acc, tier| { | |
acc[tier] += 1; | |
acc | |
}) | |
.reduce(|| [0u64; 6], |mut a, b| { | |
for i in 0..6 { | |
a[i] += b[i]; | |
} | |
a | |
}); | |
for i in 0..6 { | |
total_counts[i] += counts[i]; | |
} | |
processed += batch_size; | |
println!("Processed {} rows...", processed); | |
} | |
total_counts | |
} | |
fn main() { | |
let mut pool = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]; | |
let mut rng = thread_rng(); | |
pool.shuffle(&mut rng); | |
let winning_numbers = [pool[0], pool[1], pool[2], pool[3], pool[4], pool[5], pool[6]]; | |
let addition_number = pool[7]; | |
let winner_main = encode_bitset(&winning_numbers); | |
let addition_bit = 1 << (addition_number - 1); | |
let start = Instant::now(); | |
let counts = process_in_chunks(winner_main, addition_bit); | |
println!("\n🍀 Lotto draw:"); | |
for i in 1..=5 { | |
let suffix = match i { | |
1 => "st", | |
2 => "nd", | |
3 => "rd", | |
_ => "th", | |
}; | |
println!("🏅 {} tickets got {}{} prize.", counts[i], i, suffix); | |
} | |
println!("😢 {} tickets won nothing.", counts[0]); | |
println!("⏱️ The draw took {:.2?}", start.elapsed()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment