diff --git a/combinatoric-solver/src/collection.rs b/combinatoric-solver/src/collection.rs index d21d0f6caeb47662eff77a87f23e21cc080d7ec6..831e7a80a89aa2933ef05d30e0c47e9d6b038971 100644 --- a/combinatoric-solver/src/collection.rs +++ b/combinatoric-solver/src/collection.rs @@ -6,17 +6,22 @@ where S: Score, V: ItemVariant, { - pub score: S, - pub item_variant: [V; MAX_ITEMS], + score: S, + item_variant: [V; MAX_ITEMS], } impl<const MAX_ITEMS: usize, S: Score, V: ItemVariant> Collection<MAX_ITEMS, S, V> { #[inline] pub fn push(&self, iteration: usize, item_variant: V) -> Self { let mut new = self.clone(); + new.item_variant[iteration] = item_variant; new } + #[inline] + pub fn item_variant(&self, iteration: usize) -> &[V] { + &self.item_variant[0..iteration] + } } impl<const MAX_ITEMS: usize, S: Score, V: ItemVariant> Default for Collection<MAX_ITEMS, S, V> { diff --git a/combinatoric-solver/src/lib.rs b/combinatoric-solver/src/lib.rs index 8d79d2533a131ace47671c564057a957139b9f02..4d7930bea4208e8642fcfb7a10eece233851d46f 100644 --- a/combinatoric-solver/src/lib.rs +++ b/combinatoric-solver/src/lib.rs @@ -1,5 +1,6 @@ pub mod collection; pub mod item; +pub mod parameters; pub mod score; pub mod solver; diff --git a/combinatoric-solver/src/parameters.rs b/combinatoric-solver/src/parameters.rs new file mode 100644 index 0000000000000000000000000000000000000000..2974100acf3d8d2b0c30afb6ee2228f0a821099e --- /dev/null +++ b/combinatoric-solver/src/parameters.rs @@ -0,0 +1,8 @@ +use crate::item::ItemVariant; + +pub trait Parameters<V> +where + V: ItemVariant, +{ + fn is_valid(&self, previous_variants: &[V], new_variant: V) -> bool; +} diff --git a/combinatoric-solver/src/solver.rs b/combinatoric-solver/src/solver.rs index eb27c4730e8e9eac385bf8045d45c0e47760f057..a8f47fac2dcf04c4c187ef0d206f18ff91a7b8cf 100644 --- a/combinatoric-solver/src/solver.rs +++ b/combinatoric-solver/src/solver.rs @@ -1,8 +1,9 @@ -use std::{cmp::Reverse, collections::BinaryHeap}; +use std::{cmp::Reverse, collections::BinaryHeap, num::NonZeroUsize}; use crate::{ collection::Collection, item::{CombinatoricItem, ItemVariant}, + parameters::Parameters, score::Score, }; @@ -12,20 +13,22 @@ where V: ItemVariant, I: CombinatoricItem, { + /// Min-heap to keep the best schedules and fetch the worst of the best in O(1) top_items: BinaryHeap<Reverse<Collection<MAX_ITEMS, S, V>>>, - max_capacity: usize, + max_capacity: NonZeroUsize, parameters: Parameters, items: Vec<I>, } -impl<'a, const MAX_ITEMS: usize, S, V, I, Parameters> Solver<MAX_ITEMS, S, V, I, Parameters> +impl<'a, const MAX_ITEMS: usize, S, V, I, P> Solver<MAX_ITEMS, S, V, I, P> where S: Score, V: ItemVariant, I: CombinatoricItem<ItemVariant = V>, + P: Parameters<V>, { - pub fn new(max_capacity: usize, items: Vec<I>, parameters: Parameters) -> Self { - let top_items = BinaryHeap::with_capacity(max_capacity); + pub fn new(max_capacity: NonZeroUsize, items: Vec<I>, parameters: P) -> Self { + let top_items = BinaryHeap::with_capacity(max_capacity.into()); Self { top_items, max_capacity, @@ -35,22 +38,52 @@ where } pub fn solve(mut self) -> Vec<I> { let item = &self.items; - Self::rec_iter(0, Collection::default(), item, &mut self.top_items); + Self::rec_iter( + 0, + Collection::default(), + self.max_capacity, + &self.parameters, + item, + &mut self.top_items, + ); vec![] } fn rec_iter( i: usize, collection: Collection<MAX_ITEMS, S, V>, + max_capacity: NonZeroUsize, + parameters: &P, items: &'a [I], top_items: &mut BinaryHeap<Reverse<Collection<MAX_ITEMS, S, V>>>, ) { if i >= items.len() { + // If it reach this branch it is already valid and better than the worst top schedule, so we can push + if top_items.len() >= max_capacity.into() { + top_items.pop(); + } + top_items.push(Reverse(collection)); return; } for variant in items[i].variants() { + if !parameters.is_valid(collection.item_variant(i), variant) { + return; + } let collections = collection.push(i, variant); - Self::rec_iter(i + 1, collections, items, top_items); + // SAFETY: if the len is greater than the max_capacity, it is greater than 0 enforced by the NonZeroUsize, which allows us to unwrap without checking if it is None + if top_items.len() >= max_capacity.into() + && unsafe { top_items.peek().unwrap_unchecked().0 >= collections } + { + return; + } + Self::rec_iter( + i + 1, + collections, + max_capacity, + parameters, + items, + top_items, + ); } } }