Skip to main content

problemreductions/models/misc/
kth_largest_m_tuple.rs

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
7use crate::registry::{FieldInfo, ProblemSchemaEntry, ProblemSizeFieldEntry};
8use crate::traits::Problem;
9use crate::types::Sum;
10use serde::de::Error as _;
11use serde::{Deserialize, Deserializer, Serialize};
12
13inventory::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
29inventory::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)]
66pub struct KthLargestMTuple {
67    sets: Vec<Vec<u64>>,
68    k: u64,
69    bound: u64,
70}
71
72impl 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)]
134struct KthLargestMTupleDef {
135    sets: Vec<Vec<u64>>,
136    k: u64,
137    bound: u64,
138}
139
140impl<'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
150impl 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.
186crate::declare_variants! {
187    default KthLargestMTuple => "total_tuples * num_sets",
188}
189
190#[cfg(feature = "example-db")]
191pub(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"]
208mod tests;