PuzzleUtils/src/permutation.rs

84 lines
2.5 KiB
Rust

use std::error::Error;
fn factorial(num: usize) -> usize {
let mut fact = 1;
for n in 1..=num {
fact *= n;
}
fact
}
#[derive(Clone)]
pub struct Permutator<T: Copy + Ord> {
pub current: Vec<T>,
idx: usize,
}
impl<T: Copy + Ord> Permutator<T> {
pub fn new(elements: Vec<T>) -> Self {
Self {
current: elements,
idx: 0,
}
}
/// Explanation
///
/// there are 10! possible permutations
/// for each first number there are 9!, for each first 2 numbers 8!, etc.
/// we check how many times we have 9! permutations before we're over 1_000_000
/// aka. 1000000 / 9!
/// we take the remainder and check how many times we have 8! before we?re over it
/// (1000000 % 9!) 8!
/// etc.
/// every iteration we remove the digit by the idx from the original permutation
/// we only check for 999999 permutations because we already have the first one
///
pub fn nth_lex(mut digits: Vec<T>, nth: usize) -> Result<Vec<T>, Box<dyn Error>> {
digits.sort();
if nth == 1 {
return Ok(digits);
}
if nth > factorial(digits.len()) || nth == 0 {
return Err(Box::from("Out of bounds"));
}
let mut perm = Vec::new();
let num_unique_digits = digits.len();
let mut remainder = nth - 1;
for idx in 1..=digits.len() {
let permutations = remainder / factorial(num_unique_digits - idx);
remainder %= factorial(num_unique_digits - idx);
perm.push(digits[permutations]);
digits.remove(permutations);
}
Ok(perm)
}
}
impl<T: Copy + Ord> Iterator for Permutator<T> {
type Item = Vec<T>;
/// Returns the next permutation and changes the current permutation to it
/// This operation wraps around
fn next(&mut self) -> Option<Self::Item> {
if self.current.is_empty() {
return None;
}
let mut digits = self.current.clone();
if self.idx == factorial(digits.len()) {
return None;
}
let mut perm = Vec::new();
let num_unique_digits = digits.len();
let mut remainder = 1;
for idx in 1..=digits.len() {
let permutations = remainder / factorial(num_unique_digits - idx);
remainder %= factorial(num_unique_digits - idx);
perm.push(digits[permutations]);
digits.remove(permutations);
}
self.idx += 1;
self.current = digits;
Some(self.current.clone())
}
}