4

Given a set represented by a vector, e.g. [1,2,3], I want to iterate over all possible partitions (the order does not matter to me):

[[1], [2], [3]]
[[1,2], [3]]
[[1,3], [2]]
[[1], [2,3]]
[[1,2,3]]

I did not find a way to do this in the itertools package, neither did I find anything elsewhere. The closest I have seen was this post on StackOverflow, however in that post not all partitions are generated, e.g. [[1,3],[2]] in this case. Is there maybe some existing package to do this? Or how could I achieve that?

Edit: To clarify:

  • With "set" and "partition" I mean the mathematical notions, i.e.: a set X is a partition of a set A iff every element of X is a non-empty subset of A and every element of A appears in exactly one element of X.
  • With "representation of a set by a vector" I mean a vector that contains each element of the set exactly once, in any order. In particular, it won't contain duplicate elements.
  • What I actually want is an efficient iterator, which yields (a representation of) each possible partition exactly once. I.e. I am interested in some method create_iterator(v: Vec<T>) -> impl Iterator<Item = Vec<Vec<T>>>. I would prefer not to generate a vector of all partitions, since this has the drawbacks that it needs more space and, moreover, using the iterator I can break, which can drastically reduce runtime.
  • An example use-case could look like this:
for x in create_iterator(vec![1,2,3]) {
    println!("{:?}", x);
}
Jakob W.
  • 288
  • 1
  • 10
  • 1
    Are the elements of your slice guaranteed to be unique? If not, would you like to generate partitions based on equality, or should each element be treated as unique anyway? – Sven Marnach Aug 06 '21 at 13:52
  • unclear what you ask, I don't think there is any utils that already do that. – Stargateur Aug 06 '21 at 14:03
  • Assuming all elements are considered different, your algorithm should be as follow: for each subset of your slice containing the first element, build all partitions of the complement by recursion, then add the subset itself. Finding all subsets containing the first element is easy – you need to add the first element to each element in the powerset of the slice without the first element. – Sven Marnach Aug 06 '21 at 14:08
  • Maybe a [Bell triangle](https://en.wikipedia.org/wiki/Bell_triangle) would help? – Neil Aug 06 '21 at 19:20

2 Answers2

1

This may not be the most efficient in terms of heap allocation, but it works! Here's the playground.


fn get_partitions(input: &mut Vec<u64>) -> Vec<Vec<Vec<u64>>> {
    if input.len() == 0 {
        return Vec::new();
    }
    if input.len() == 1 {
        return vec![vec![vec![input[0]]]];
    }
    else {
        let a = input.pop().unwrap();
        let partitions = get_partitions(input);
        let mut output = Vec::new();
        for part in partitions {
            let mut tmp = part.to_vec();
            tmp.push(vec![a]);
            output.push(tmp);
            for idx in 0..part.len() {
                let mut tmp = part.to_vec();
                tmp[idx].push(a);
                output.push(tmp);
            }
        }
        return output;
    }
}

fn main() {
    let mut input = vec![1,2,3];
    let output = get_partitions(&mut input);
    println!("{:?}", output);
}

I found the answer here, which outlines the algorithm.

Ian Graham
  • 346
  • 1
  • 11
  • 1
    Thanks! However, I would prefer a more efficient implementation where not all partitions are generated. I'm sorry that I just made the edit while you already posted the answer. Anyways, I appreciate your answer, especially the linked post, as it seems like the second answer there could be made into an efficient iterator! – Jakob W. Aug 06 '21 at 19:57
  • 1
    Ah, no worries! – Ian Graham Aug 06 '21 at 19:58
  • 1
    Oh wow, I just glanced the second answer since it looked long and wordy, but looking at it now it's WAY better for your use case! Glad that it could be of help! – Ian Graham Aug 06 '21 at 20:08
0

In the end I just implemented the highest-ranked answer here. I am still new to the language, and hence the implementation may not be the best. In particular, the lifetime parameters were a bit messy. So, comments are highly appreciated! :)

use itertools::zip;

// Based on https://stackoverflow.com/a/30898130/4322240. Read first if something here is unclear!

// Representation of a state in which the iterator can be.
// elements: contains the elements to be partitioned.
// index_map: in terms of the linked post, index_map[i] = p_i (but 0-indexed)
// initialized: marks if the Partition element was just initialized and hence
//     was not generated by next()
pub struct Partition<'a, T> where T: Copy {
    elements: &'a Vec<T>,
    index_map: Vec<usize>,
    initialized: bool, 
}
impl<'a, T> Partition<'a, T> where T: Copy {
    fn smallest_viable(v: &'a Vec<T>) -> Partition<'a, T> {
        Partition::<T> { elements: v, index_map: vec![0; v.len()], initialized: true }
    }
    fn is_viable(&self) -> bool {
        self.elements.len() == self.index_map.len()
        && (self.index_map.is_empty() || self.index_map[0] == 0)
        && (1..self.index_map.len())
                .all(|x| (0..x).any(|y| self.index_map[x] <= self.index_map[y] + 1))
    }
    fn is_incrementable(&self, index: usize) -> bool {
        index <= self.index_map.len()
        && index > 0
        // p_i <= n does not need to be checked, as it is guaranteed if the invariant
        // "for all j > 0 there exists i < j s.t. p_j <= p_i + 1" is satisfied
        && (0..index).any(|x| self.index_map[x] == self.index_map[index])
    }
    fn increment(&mut self, index: usize) {
        self.index_map[index] += 1;
        for x in index + 1..self.index_map.len() {
            self.index_map[x] = 0;
        }
    }
    fn create_partition(&self) -> Vec<Vec<T>> {
        if let Some(&max_index) = self.index_map.iter().max(){
            return (0..=max_index).map(|x| zip(self.elements, &self.index_map)
                    .filter(|(_e,i)| **i == x).map(|(e,_i)| *e).collect())
                    .collect();
        } else {
            return Vec::new();
        }
    }
}
impl<'a, T> Iterator for Partition<'a, T> where T: Copy {
    type Item = Vec<Vec<T>>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.initialized {
            self.initialized = false;
            return Some(self.create_partition());
        }
        for index in (0..self.index_map.len()).rev() {
            if self.is_incrementable(index) {
                self.increment(index);
                return Some(self.create_partition());
            }
        }
        return None;
    }
}

pub fn partitions<'a, T>(v: &'a Vec<T>) -> Partition<'a, T> where T: Copy {
    Partition::smallest_viable(v)
}

Example usage:

fn main() {
    for x in partitions(&vec![7,8,9]) {
        println!("{:?}", x);
    }
}

outputs

[[7, 8, 9]]
[[7, 8], [9]]
[[7, 9], [8]]
[[7], [8, 9]]
[[7], [8], [9]]
Jakob W.
  • 288
  • 1
  • 10