|
| 1 | +//! Kth Largest m-Tuple problem implementation. |
| 2 | +//! |
| 3 | +//! Given m sets of positive integers and thresholds K and B, count how many |
| 4 | +//! distinct m-tuples (one element per set) have total size at least B. |
| 5 | +//! The answer is YES iff the count is at least K. Garey & Johnson MP10. |
| 6 | +
|
| 7 | +use crate::registry::{FieldInfo, ProblemSchemaEntry, ProblemSizeFieldEntry}; |
| 8 | +use crate::traits::Problem; |
| 9 | +use crate::types::Sum; |
| 10 | +use serde::de::Error as _; |
| 11 | +use serde::{Deserialize, Deserializer, Serialize}; |
| 12 | + |
| 13 | +inventory::submit! { |
| 14 | + ProblemSchemaEntry { |
| 15 | + name: "KthLargestMTuple", |
| 16 | + display_name: "Kth Largest m-Tuple", |
| 17 | + aliases: &[], |
| 18 | + dimensions: &[], |
| 19 | + module_path: module_path!(), |
| 20 | + description: "Count m-tuples whose total size meets a bound and compare against a threshold K", |
| 21 | + fields: &[ |
| 22 | + FieldInfo { name: "sets", type_name: "Vec<Vec<u64>>", description: "m sets, each containing positive integer sizes" }, |
| 23 | + FieldInfo { name: "k", type_name: "u64", description: "Threshold K (answer YES iff count >= K)" }, |
| 24 | + FieldInfo { name: "bound", type_name: "u64", description: "Lower bound B on tuple sum" }, |
| 25 | + ], |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +inventory::submit! { |
| 30 | + ProblemSizeFieldEntry { |
| 31 | + name: "KthLargestMTuple", |
| 32 | + fields: &["num_sets", "total_tuples"], |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +/// The Kth Largest m-Tuple problem. |
| 37 | +/// |
| 38 | +/// Given sets `X_1, ..., X_m` of positive integers, a threshold `K`, and a |
| 39 | +/// bound `B`, count how many distinct m-tuples `(x_1, ..., x_m)` in |
| 40 | +/// `X_1 x ... x X_m` satisfy `sum(x_i) >= B`. The answer is YES iff the |
| 41 | +/// count is at least `K`. |
| 42 | +/// |
| 43 | +/// # Representation |
| 44 | +/// |
| 45 | +/// Variable `i` selects an element from set `X_i`, ranging over `{0, ..., |X_i|-1}`. |
| 46 | +/// `evaluate` returns `Sum(1)` if the tuple sum >= B, else `Sum(0)`. |
| 47 | +/// The aggregate over all configurations gives the total count of qualifying tuples. |
| 48 | +/// |
| 49 | +/// # Example |
| 50 | +/// |
| 51 | +/// ``` |
| 52 | +/// use problemreductions::models::misc::KthLargestMTuple; |
| 53 | +/// use problemreductions::{Problem, Solver, BruteForce}; |
| 54 | +/// |
| 55 | +/// let problem = KthLargestMTuple::new( |
| 56 | +/// vec![vec![2, 5, 8], vec![3, 6], vec![1, 4, 7]], |
| 57 | +/// 14, |
| 58 | +/// 12, |
| 59 | +/// ); |
| 60 | +/// let solver = BruteForce::new(); |
| 61 | +/// let value = solver.solve(&problem); |
| 62 | +/// // 14 of the 18 tuples have sum >= 12 |
| 63 | +/// assert_eq!(value, problemreductions::types::Sum(14)); |
| 64 | +/// ``` |
| 65 | +#[derive(Debug, Clone, Serialize)] |
| 66 | +pub struct KthLargestMTuple { |
| 67 | + sets: Vec<Vec<u64>>, |
| 68 | + k: u64, |
| 69 | + bound: u64, |
| 70 | +} |
| 71 | + |
| 72 | +impl KthLargestMTuple { |
| 73 | + fn validate(sets: &[Vec<u64>], k: u64, bound: u64) -> Result<(), String> { |
| 74 | + if sets.is_empty() { |
| 75 | + return Err("KthLargestMTuple requires at least one set".to_string()); |
| 76 | + } |
| 77 | + if sets.iter().any(|s| s.is_empty()) { |
| 78 | + return Err("Every set must be non-empty".to_string()); |
| 79 | + } |
| 80 | + if sets.iter().any(|s| s.contains(&0)) { |
| 81 | + return Err("All sizes must be positive (> 0)".to_string()); |
| 82 | + } |
| 83 | + if k == 0 { |
| 84 | + return Err("Threshold K must be positive".to_string()); |
| 85 | + } |
| 86 | + if bound == 0 { |
| 87 | + return Err("Bound B must be positive".to_string()); |
| 88 | + } |
| 89 | + Ok(()) |
| 90 | + } |
| 91 | + |
| 92 | + /// Try to create a new KthLargestMTuple instance. |
| 93 | + pub fn try_new(sets: Vec<Vec<u64>>, k: u64, bound: u64) -> Result<Self, String> { |
| 94 | + Self::validate(&sets, k, bound)?; |
| 95 | + Ok(Self { sets, k, bound }) |
| 96 | + } |
| 97 | + |
| 98 | + /// Create a new KthLargestMTuple instance. |
| 99 | + /// |
| 100 | + /// # Panics |
| 101 | + /// |
| 102 | + /// Panics if the inputs are invalid. |
| 103 | + pub fn new(sets: Vec<Vec<u64>>, k: u64, bound: u64) -> Self { |
| 104 | + Self::try_new(sets, k, bound).unwrap_or_else(|msg| panic!("{msg}")) |
| 105 | + } |
| 106 | + |
| 107 | + /// Returns the sets. |
| 108 | + pub fn sets(&self) -> &[Vec<u64>] { |
| 109 | + &self.sets |
| 110 | + } |
| 111 | + |
| 112 | + /// Returns the threshold K. |
| 113 | + pub fn k(&self) -> u64 { |
| 114 | + self.k |
| 115 | + } |
| 116 | + |
| 117 | + /// Returns the bound B. |
| 118 | + pub fn bound(&self) -> u64 { |
| 119 | + self.bound |
| 120 | + } |
| 121 | + |
| 122 | + /// Returns the number of sets (m). |
| 123 | + pub fn num_sets(&self) -> usize { |
| 124 | + self.sets.len() |
| 125 | + } |
| 126 | + |
| 127 | + /// Returns the total number of m-tuples (product of set sizes). |
| 128 | + pub fn total_tuples(&self) -> usize { |
| 129 | + self.sets.iter().map(|s| s.len()).product() |
| 130 | + } |
| 131 | +} |
| 132 | + |
| 133 | +#[derive(Deserialize)] |
| 134 | +struct KthLargestMTupleDef { |
| 135 | + sets: Vec<Vec<u64>>, |
| 136 | + k: u64, |
| 137 | + bound: u64, |
| 138 | +} |
| 139 | + |
| 140 | +impl<'de> Deserialize<'de> for KthLargestMTuple { |
| 141 | + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
| 142 | + where |
| 143 | + D: Deserializer<'de>, |
| 144 | + { |
| 145 | + let data = KthLargestMTupleDef::deserialize(deserializer)?; |
| 146 | + Self::try_new(data.sets, data.k, data.bound).map_err(D::Error::custom) |
| 147 | + } |
| 148 | +} |
| 149 | + |
| 150 | +impl Problem for KthLargestMTuple { |
| 151 | + const NAME: &'static str = "KthLargestMTuple"; |
| 152 | + type Value = Sum<u64>; |
| 153 | + |
| 154 | + fn variant() -> Vec<(&'static str, &'static str)> { |
| 155 | + crate::variant_params![] |
| 156 | + } |
| 157 | + |
| 158 | + fn dims(&self) -> Vec<usize> { |
| 159 | + self.sets.iter().map(|s| s.len()).collect() |
| 160 | + } |
| 161 | + |
| 162 | + fn evaluate(&self, config: &[usize]) -> Sum<u64> { |
| 163 | + if config.len() != self.num_sets() { |
| 164 | + return Sum(0); |
| 165 | + } |
| 166 | + for (i, &choice) in config.iter().enumerate() { |
| 167 | + if choice >= self.sets[i].len() { |
| 168 | + return Sum(0); |
| 169 | + } |
| 170 | + } |
| 171 | + let total: u64 = config |
| 172 | + .iter() |
| 173 | + .enumerate() |
| 174 | + .map(|(i, &choice)| self.sets[i][choice]) |
| 175 | + .sum(); |
| 176 | + if total >= self.bound { |
| 177 | + Sum(1) |
| 178 | + } else { |
| 179 | + Sum(0) |
| 180 | + } |
| 181 | + } |
| 182 | +} |
| 183 | + |
| 184 | +// Best known: brute-force enumeration of all tuples, O(total_tuples * num_sets). |
| 185 | +// No sub-exponential exact algorithm is known for the general case. |
| 186 | +crate::declare_variants! { |
| 187 | + default KthLargestMTuple => "total_tuples * num_sets", |
| 188 | +} |
| 189 | + |
| 190 | +#[cfg(feature = "example-db")] |
| 191 | +pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> { |
| 192 | + // m=3, X_1={2,5,8}, X_2={3,6}, X_3={1,4,7}, B=12, K=14. |
| 193 | + // 14 of 18 tuples have sum >= 12. The config [2,1,2] picks (8,6,7) with sum=21 >= 12. |
| 194 | + vec![crate::example_db::specs::ModelExampleSpec { |
| 195 | + id: "kth_largest_m_tuple", |
| 196 | + instance: Box::new(KthLargestMTuple::new( |
| 197 | + vec![vec![2, 5, 8], vec![3, 6], vec![1, 4, 7]], |
| 198 | + 14, |
| 199 | + 12, |
| 200 | + )), |
| 201 | + optimal_config: vec![2, 1, 2], |
| 202 | + optimal_value: serde_json::json!(1), |
| 203 | + }] |
| 204 | +} |
| 205 | + |
| 206 | +#[cfg(test)] |
| 207 | +#[path = "../../unit_tests/models/misc/kth_largest_m_tuple.rs"] |
| 208 | +mod tests; |
0 commit comments