Skip to main content

algebraeon_sets/combinatorics/
subsets.rs

1/// Provides an iterator over the k-element subsets of {0, 1, ..., n-1} in lexicographic order such that some elements of {0, 1, ..., n-1} can be excluded from future subsets at any point during the iteration.
2#[derive(Debug)]
3pub struct LexicographicSubsetsWithRemovals {
4    n: usize,
5    all_items_idx: Vec<Option<usize>>,
6    remaining_items: Vec<usize>,
7    subset: Vec<usize>,
8    finished: bool,
9}
10
11impl LexicographicSubsetsWithRemovals {
12    /// Constructor
13    pub fn new(n: usize, k: usize) -> Self {
14        Self {
15            n,
16            all_items_idx: (0..n).map(Some).collect(),
17            remaining_items: (0..n).collect(),
18            subset: (0..k).collect(),
19            finished: k > n,
20        }
21    }
22
23    /// Exclude the provided element from appearing in any further subsets.
24    pub fn exclude(&mut self, a: usize) {
25        assert!(a < self.all_items_idx.len());
26        if self.all_items_idx[a].is_none() {
27            // Already excluded
28            return;
29        }
30
31        while self.subset.iter().any(|x| self.remaining_items[*x] == a) {
32            match self.next() {
33                Some(_) => {}
34                None => {
35                    break;
36                }
37            }
38        }
39
40        if !self.finished {
41            let a_idx = self.all_items_idx[a].unwrap();
42            self.remaining_items.remove(a_idx);
43            self.all_items_idx[a] = None;
44            for i in (a + 1)..self.all_items_idx.len() {
45                if let Some(idx) = self.all_items_idx[i].as_mut() {
46                    *idx -= 1;
47                }
48            }
49            for x in &mut self.subset {
50                match (*x).cmp(&a_idx) {
51                    std::cmp::Ordering::Less => {}
52                    std::cmp::Ordering::Equal => {
53                        unreachable!()
54                    }
55                    std::cmp::Ordering::Greater => {
56                        *x -= 1;
57                    }
58                }
59            }
60            self.n -= 1;
61        }
62    }
63}
64
65impl Iterator for LexicographicSubsetsWithRemovals {
66    type Item = Vec<usize>;
67
68    fn next(&mut self) -> Option<Self::Item> {
69        let next = if self.finished {
70            return None;
71        } else {
72            Some(
73                self.subset
74                    .iter()
75                    .map(|i| self.remaining_items[*i])
76                    .collect(),
77            )
78        };
79
80        let k = self.subset.len();
81        let i = 'FIND_I: {
82            for (i, s) in self.subset.iter().rev().enumerate() {
83                if *s < self.n - i - 1 {
84                    break 'FIND_I Some(i);
85                }
86            }
87            None
88        };
89        match i {
90            Some(i) => {
91                self.subset[k - 1 - i] += 1;
92                for j in 0..i {
93                    self.subset[k - i + j] = self.subset[k - i - 1] + 1 + j;
94                }
95            }
96            None => {
97                self.finished = true;
98            }
99        }
100
101        next
102    }
103}
104
105/// Returns all size k subsets of {0, 1, ..., n-1}.
106/// ```
107/// use algebraeon_sets::combinatorics::subsets;
108/// assert_eq!(subsets(5, 3).collect::<Vec<_>>(), vec![
109///     vec![0, 1, 2],
110///     vec![0, 1, 3],
111///     vec![0, 1, 4],
112///     vec![0, 2, 3],
113///     vec![0, 2, 4],
114///     vec![0, 3, 4],
115///     vec![1, 2, 3],
116///     vec![1, 2, 4],
117///     vec![1, 3, 4],
118///     vec![2, 3, 4],
119/// ]);
120/// ```
121pub fn subsets(n: usize, k: usize) -> impl Iterator<Item = Vec<usize>> {
122    LexicographicSubsetsWithRemovals::new(n, k)
123}
124
125/// Returns all subsets of {0, 1, ..., n-1}.
126/// ```
127/// use algebraeon_sets::combinatorics::all_subsets;
128/// assert_eq!(all_subsets(3).collect::<Vec<_>>(), vec![
129///     vec![],
130///     vec![0],
131///     vec![1],
132///     vec![0, 1],
133///     vec![2],
134///     vec![0, 2],
135///     vec![1, 2],
136///     vec![0, 1, 2],
137/// ]);
138/// ```
139pub fn all_subsets(n: usize) -> impl Iterator<Item = Vec<usize>> {
140    (0usize..(1 << n)).map(move |i| (0..n).filter(|j| i & (1 << j) != 0).collect())
141}
142
143/// Returns all size k subsets of items.
144/// ```
145/// use algebraeon_sets::combinatorics::subsets_of_vec;
146/// assert_eq!(subsets_of_vec(vec!["a", "b", "c"], 2).collect::<Vec<_>>(), vec![
147///     vec!["a", "b"],
148///     vec!["a", "c"],
149///     vec!["b", "c"],
150/// ]);
151/// ```
152pub fn subsets_of_vec<'a, T: 'a + Clone>(
153    items: Vec<T>,
154    k: usize,
155) -> impl 'a + Iterator<Item = Vec<T>> {
156    subsets(items.len(), k)
157        .map(move |subset| subset.into_iter().map(|idx| items[idx].clone()).collect())
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    pub fn lexicographic_subsets_with_removals_test_edge_cases() {
166        let x = subsets(0, 1).collect::<Vec<_>>();
167        println!("{:?}", x);
168        assert_eq!(x.len(), 0);
169
170        let x = subsets(3, 5).collect::<Vec<_>>();
171        println!("{:?}", x);
172        assert_eq!(x.len(), 0);
173
174        let x = subsets(3, 3).collect::<Vec<_>>();
175        println!("{:?}", x);
176        assert_eq!(x.len(), 1);
177    }
178
179    #[test]
180    pub fn lexicographic_subsets_with_removals_test_1() {
181        let mut c = LexicographicSubsetsWithRemovals::new(7, 3);
182        for _ in 0..19 {
183            let x = c.next().unwrap();
184            println!("{:?}", x);
185        }
186
187        println!("rm 4");
188        c.exclude(4);
189        println!("rm 5");
190        c.exclude(5);
191
192        for _ in 0..2 {
193            let x = c.next().unwrap();
194            println!("{:?}", x);
195        }
196
197        assert_eq!(c.next(), None);
198    }
199
200    #[test]
201    pub fn lexicographic_subsets_with_removals_test_2() {
202        let mut c = LexicographicSubsetsWithRemovals::new(7, 3);
203        c.exclude(0);
204        c.exclude(1);
205        c.exclude(2);
206        c.exclude(3);
207        c.exclude(4);
208        debug_assert_eq!(c.next(), None);
209    }
210
211    #[test]
212    pub fn run() {
213        println!("{:?}", subsets(5, 3).collect::<Vec<_>>());
214        assert_eq!(subsets(5, 3).collect::<Vec<_>>().len(), 10);
215    }
216}