scirs2_optimize/combinatorial/
covering.rs1use crate::error::OptimizeError;
9
10pub type CoveringResult<T> = Result<T, OptimizeError>;
12
13pub fn greedy_set_cover(universe: usize, sets: &[Vec<usize>]) -> Vec<usize> {
22 if universe == 0 || sets.is_empty() {
23 return vec![];
24 }
25
26 let mut uncovered = vec![true; universe];
27 let mut remaining = universe;
28 let mut selected = Vec::new();
29 let mut used = vec![false; sets.len()];
30
31 while remaining > 0 {
32 let mut best_idx = None;
34 let mut best_count = 0usize;
35
36 for (i, set) in sets.iter().enumerate() {
37 if used[i] {
38 continue;
39 }
40 let count = set
41 .iter()
42 .filter(|&&e| e < universe && uncovered[e])
43 .count();
44 if count > best_count {
45 best_count = count;
46 best_idx = Some(i);
47 }
48 }
49
50 match best_idx {
51 Some(idx) => {
52 used[idx] = true;
53 selected.push(idx);
54 for &e in &sets[idx] {
55 if e < universe && uncovered[e] {
56 uncovered[e] = false;
57 remaining -= 1;
58 }
59 }
60 }
61 None => break, }
63 }
64
65 selected
66}
67
68pub fn weighted_set_cover(
77 universe: usize,
78 sets: &[Vec<usize>],
79 costs: &[f64],
80) -> CoveringResult<(Vec<usize>, f64)> {
81 if sets.len() != costs.len() {
82 return Err(OptimizeError::InvalidInput(format!(
83 "sets.len() = {} but costs.len() = {}",
84 sets.len(),
85 costs.len()
86 )));
87 }
88 if universe == 0 || sets.is_empty() {
89 return Ok((vec![], 0.0));
90 }
91
92 let mut uncovered = vec![true; universe];
93 let mut remaining = universe;
94 let mut selected = Vec::new();
95 let mut total_cost = 0.0;
96 let mut used = vec![false; sets.len()];
97
98 while remaining > 0 {
99 let mut best_idx = None;
100 let mut best_ratio = f64::INFINITY;
101
102 for (i, set) in sets.iter().enumerate() {
103 if used[i] {
104 continue;
105 }
106 let new_covers = set
107 .iter()
108 .filter(|&&e| e < universe && uncovered[e])
109 .count();
110 if new_covers == 0 {
111 continue;
112 }
113 let ratio = costs[i] / new_covers as f64;
114 if ratio < best_ratio {
115 best_ratio = ratio;
116 best_idx = Some(i);
117 }
118 }
119
120 match best_idx {
121 Some(idx) => {
122 used[idx] = true;
123 selected.push(idx);
124 total_cost += costs[idx];
125 for &e in &sets[idx] {
126 if e < universe && uncovered[e] {
127 uncovered[e] = false;
128 remaining -= 1;
129 }
130 }
131 }
132 None => break,
133 }
134 }
135
136 Ok((selected, total_cost))
137}
138
139pub fn vertex_cover_2approx(n: usize, edges: &[(usize, usize)]) -> Vec<usize> {
146 if n == 0 || edges.is_empty() {
147 return vec![];
148 }
149
150 let mut in_cover = vec![false; n];
151 let mut cover = Vec::new();
152
153 for &(u, v) in edges {
154 if u >= n || v >= n {
155 continue;
156 }
157 if !in_cover[u] && !in_cover[v] {
159 in_cover[u] = true;
160 in_cover[v] = true;
161 cover.push(u);
162 cover.push(v);
163 }
164 }
165
166 cover.sort_unstable();
167 cover.dedup();
168 cover
169}
170
171pub fn min_vertex_cover_bip(n_left: usize, n_right: usize, edges: &[(usize, usize)]) -> Vec<usize> {
185 if n_left == 0 || n_right == 0 {
186 return vec![];
187 }
188
189 let mut adj_left: Vec<Vec<usize>> = vec![vec![]; n_left];
191 for &(u, v) in edges {
192 if u < n_left && v < n_right {
193 adj_left[u].push(v);
194 }
195 }
196
197 let mut match_left = vec![usize::MAX; n_left];
199 let mut match_right = vec![usize::MAX; n_right];
200
201 for u in 0..n_left {
202 let mut visited = vec![false; n_right];
203 augment_bip(
204 u,
205 &adj_left,
206 &mut match_left,
207 &mut match_right,
208 &mut visited,
209 );
210 }
211
212 let mut reachable_left = vec![false; n_left];
221 let mut reachable_right = vec![false; n_right];
222
223 let mut stack: Vec<(bool, usize)> = Vec::new(); for u in 0..n_left {
226 if match_left[u] == usize::MAX {
227 stack.push((true, u));
228 reachable_left[u] = true;
229 }
230 }
231
232 while let Some((is_left, v)) = stack.pop() {
233 if is_left {
234 for &r in &adj_left[v] {
236 if !reachable_right[r] {
237 reachable_right[r] = true;
238 stack.push((false, r));
239 }
240 }
241 } else {
242 let ml = match_right[v];
244 if ml != usize::MAX && !reachable_left[ml] {
245 reachable_left[ml] = true;
246 stack.push((true, ml));
247 }
248 }
249 }
250
251 let mut cover = Vec::new();
253 for u in 0..n_left {
254 if !reachable_left[u] {
255 cover.push(u); }
257 }
258 for v in 0..n_right {
259 if reachable_right[v] {
260 cover.push(n_left + v); }
262 }
263
264 cover.sort_unstable();
265 cover
266}
267
268fn augment_bip(
270 u: usize,
271 adj: &[Vec<usize>],
272 match_left: &mut Vec<usize>,
273 match_right: &mut Vec<usize>,
274 visited: &mut Vec<bool>,
275) -> bool {
276 for &v in &adj[u] {
277 if visited[v] {
278 continue;
279 }
280 visited[v] = true;
281 let prev = match_right[v];
282 if prev == usize::MAX || augment_bip(prev, adj, match_left, match_right, visited) {
283 match_left[u] = v;
284 match_right[v] = u;
285 return true;
286 }
287 }
288 false
289}
290
291pub fn hitting_set(universe: usize, sets: &[Vec<usize>]) -> Vec<usize> {
301 if universe == 0 || sets.is_empty() {
302 return vec![];
303 }
304
305 let m = sets.len();
306 let mut hit = vec![false; m]; let mut remaining = m;
308 let mut chosen = Vec::new();
309 let mut in_hitting_set = vec![false; universe];
310
311 while remaining > 0 {
312 let mut best_elem = None;
314 let mut best_count = 0usize;
315
316 for e in 0..universe {
317 if in_hitting_set[e] {
318 continue;
319 }
320 let count = sets
321 .iter()
322 .enumerate()
323 .filter(|(i, s)| !hit[*i] && s.contains(&e))
324 .count();
325 if count > best_count {
326 best_count = count;
327 best_elem = Some(e);
328 }
329 }
330
331 match best_elem {
332 Some(e) => {
333 in_hitting_set[e] = true;
334 chosen.push(e);
335 for (i, s) in sets.iter().enumerate() {
336 if !hit[i] && s.contains(&e) {
337 hit[i] = true;
338 remaining -= 1;
339 }
340 }
341 }
342 None => break, }
344 }
345
346 chosen.sort_unstable();
347 chosen
348}
349
350#[cfg(test)]
354mod tests {
355 use super::*;
356
357 fn is_set_cover(universe: usize, sets: &[Vec<usize>], selected: &[usize]) -> bool {
358 let mut covered = vec![false; universe];
359 for &idx in selected {
360 for &e in &sets[idx] {
361 if e < universe {
362 covered[e] = true;
363 }
364 }
365 }
366 covered.iter().all(|&c| c)
367 }
368
369 #[test]
370 fn test_greedy_set_cover() {
371 let sets = vec![vec![0, 1, 2], vec![1, 3], vec![2, 4], vec![3, 4]];
372 let sel = greedy_set_cover(5, &sets);
373 assert!(is_set_cover(5, &sets, &sel));
374 assert!(!sel.is_empty());
375 }
376
377 #[test]
378 fn test_weighted_set_cover() {
379 let sets = vec![vec![0, 1, 2], vec![1, 3], vec![2, 4], vec![3, 4]];
380 let costs = vec![3.0, 1.0, 1.0, 1.0];
381 let (sel, cost) = weighted_set_cover(5, &sets, &costs).expect("unexpected None or Err");
382 assert!(is_set_cover(5, &sets, &sel));
383 assert!(cost > 0.0);
384 }
385
386 #[test]
387 fn test_weighted_set_cover_mismatch() {
388 let sets = vec![vec![0, 1]];
389 let costs = vec![1.0, 2.0]; assert!(weighted_set_cover(2, &sets, &costs).is_err());
391 }
392
393 #[test]
394 fn test_vertex_cover_2approx() {
395 let edges = vec![(0, 1), (1, 2), (2, 0)];
397 let cover = vertex_cover_2approx(3, &edges);
398 for &(u, v) in &edges {
400 assert!(cover.contains(&u) || cover.contains(&v));
401 }
402 }
403
404 #[test]
405 fn test_vertex_cover_path() {
406 let edges = vec![(0, 1), (1, 2), (2, 3)];
408 let cover = vertex_cover_2approx(4, &edges);
409 for &(u, v) in &edges {
410 assert!(cover.contains(&u) || cover.contains(&v));
411 }
412 assert!(cover.len() <= 4); }
414
415 #[test]
416 fn test_kings_bipartite() {
417 let edges = vec![(0, 0), (0, 1), (1, 0), (1, 1)];
419 let cover = min_vertex_cover_bip(2, 2, &edges);
420 for &(u, v) in &edges {
422 assert!(cover.contains(&u) || cover.contains(&(2 + v)));
423 }
424 assert_eq!(cover.len(), 2);
425 }
426
427 #[test]
428 fn test_kings_path_bip() {
429 let edges = vec![(0, 0)];
431 let cover = min_vertex_cover_bip(1, 1, &edges);
432 assert_eq!(cover.len(), 1);
433 assert!(cover.contains(&0) || cover.contains(&1));
434 }
435
436 #[test]
437 fn test_hitting_set() {
438 let sets = vec![vec![0, 1], vec![1, 2], vec![2, 3], vec![3, 4]];
440 let hs = hitting_set(5, &sets);
441 for s in &sets {
443 assert!(s.iter().any(|e| hs.contains(e)));
444 }
445 }
446
447 #[test]
448 fn test_empty_inputs() {
449 assert!(greedy_set_cover(0, &[]).is_empty());
450 assert!(vertex_cover_2approx(0, &[]).is_empty());
451 assert!(hitting_set(0, &[]).is_empty());
452 }
453}